fix a screw up in r258371
Pointyhat to: avg
This commit is contained in:
parent
16f9dc06c8
commit
50f0ffdcc4
159
uts/common/fs/zfs/space_reftree.c
Normal file
159
uts/common/fs/zfs/space_reftree.c
Normal file
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2013 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <sys/zfs_context.h>
|
||||
#include <sys/range_tree.h>
|
||||
#include <sys/space_reftree.h>
|
||||
|
||||
/*
|
||||
* Space reference trees.
|
||||
*
|
||||
* A range tree is a collection of integers. Every integer is either
|
||||
* in the tree, or it's not. A space reference tree generalizes
|
||||
* the idea: it allows its members to have arbitrary reference counts,
|
||||
* as opposed to the implicit reference count of 0 or 1 in a range tree.
|
||||
* This representation comes in handy when computing the union or
|
||||
* intersection of multiple space maps. For example, the union of
|
||||
* N range trees is the subset of the reference tree with refcnt >= 1.
|
||||
* The intersection of N range trees is the subset with refcnt >= N.
|
||||
*
|
||||
* [It's very much like a Fourier transform. Unions and intersections
|
||||
* are hard to perform in the 'range tree domain', so we convert the trees
|
||||
* into the 'reference count domain', where it's trivial, then invert.]
|
||||
*
|
||||
* vdev_dtl_reassess() uses computations of this form to determine
|
||||
* DTL_MISSING and DTL_OUTAGE for interior vdevs -- e.g. a RAID-Z vdev
|
||||
* has an outage wherever refcnt >= vdev_nparity + 1, and a mirror vdev
|
||||
* has an outage wherever refcnt >= vdev_children.
|
||||
*/
|
||||
static int
|
||||
space_reftree_compare(const void *x1, const void *x2)
|
||||
{
|
||||
const space_ref_t *sr1 = x1;
|
||||
const space_ref_t *sr2 = x2;
|
||||
|
||||
if (sr1->sr_offset < sr2->sr_offset)
|
||||
return (-1);
|
||||
if (sr1->sr_offset > sr2->sr_offset)
|
||||
return (1);
|
||||
|
||||
if (sr1 < sr2)
|
||||
return (-1);
|
||||
if (sr1 > sr2)
|
||||
return (1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
space_reftree_create(avl_tree_t *t)
|
||||
{
|
||||
avl_create(t, space_reftree_compare,
|
||||
sizeof (space_ref_t), offsetof(space_ref_t, sr_node));
|
||||
}
|
||||
|
||||
void
|
||||
space_reftree_destroy(avl_tree_t *t)
|
||||
{
|
||||
space_ref_t *sr;
|
||||
void *cookie = NULL;
|
||||
|
||||
while ((sr = avl_destroy_nodes(t, &cookie)) != NULL)
|
||||
kmem_free(sr, sizeof (*sr));
|
||||
|
||||
avl_destroy(t);
|
||||
}
|
||||
|
||||
static void
|
||||
space_reftree_add_node(avl_tree_t *t, uint64_t offset, int64_t refcnt)
|
||||
{
|
||||
space_ref_t *sr;
|
||||
|
||||
sr = kmem_alloc(sizeof (*sr), KM_SLEEP);
|
||||
sr->sr_offset = offset;
|
||||
sr->sr_refcnt = refcnt;
|
||||
|
||||
avl_add(t, sr);
|
||||
}
|
||||
|
||||
void
|
||||
space_reftree_add_seg(avl_tree_t *t, uint64_t start, uint64_t end,
|
||||
int64_t refcnt)
|
||||
{
|
||||
space_reftree_add_node(t, start, refcnt);
|
||||
space_reftree_add_node(t, end, -refcnt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert (or add) a range tree into a reference tree.
|
||||
*/
|
||||
void
|
||||
space_reftree_add_map(avl_tree_t *t, range_tree_t *rt, int64_t refcnt)
|
||||
{
|
||||
range_seg_t *rs;
|
||||
|
||||
ASSERT(MUTEX_HELD(rt->rt_lock));
|
||||
|
||||
for (rs = avl_first(&rt->rt_root); rs; rs = AVL_NEXT(&rt->rt_root, rs))
|
||||
space_reftree_add_seg(t, rs->rs_start, rs->rs_end, refcnt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a reference tree into a range tree. The range tree will contain
|
||||
* all members of the reference tree for which refcnt >= minref.
|
||||
*/
|
||||
void
|
||||
space_reftree_generate_map(avl_tree_t *t, range_tree_t *rt, int64_t minref)
|
||||
{
|
||||
uint64_t start = -1ULL;
|
||||
int64_t refcnt = 0;
|
||||
space_ref_t *sr;
|
||||
|
||||
ASSERT(MUTEX_HELD(rt->rt_lock));
|
||||
|
||||
range_tree_vacate(rt, NULL, NULL);
|
||||
|
||||
for (sr = avl_first(t); sr != NULL; sr = AVL_NEXT(t, sr)) {
|
||||
refcnt += sr->sr_refcnt;
|
||||
if (refcnt >= minref) {
|
||||
if (start == -1ULL) {
|
||||
start = sr->sr_offset;
|
||||
}
|
||||
} else {
|
||||
if (start != -1ULL) {
|
||||
uint64_t end = sr->sr_offset;
|
||||
ASSERT(start <= end);
|
||||
if (end > start)
|
||||
range_tree_add(rt, start, end - start);
|
||||
start = -1ULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
ASSERT(refcnt == 0);
|
||||
ASSERT(start == -1ULL);
|
||||
}
|
96
uts/common/fs/zfs/sys/range_tree.h
Normal file
96
uts/common/fs/zfs/sys/range_tree.h
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2013 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _SYS_RANGE_TREE_H
|
||||
#define _SYS_RANGE_TREE_H
|
||||
|
||||
#include <sys/avl.h>
|
||||
#include <sys/dmu.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define RANGE_TREE_HISTOGRAM_SIZE 64
|
||||
|
||||
typedef struct range_tree_ops range_tree_ops_t;
|
||||
|
||||
typedef struct range_tree {
|
||||
avl_tree_t rt_root; /* offset-ordered segment AVL tree */
|
||||
uint64_t rt_space; /* sum of all segments in the map */
|
||||
range_tree_ops_t *rt_ops;
|
||||
void *rt_arg;
|
||||
|
||||
/*
|
||||
* The rt_histogram maintains a histogram of ranges. Each bucket,
|
||||
* rt_histogram[i], contains the number of ranges whose size is:
|
||||
* 2^i <= size of range in bytes < 2^(i+1)
|
||||
*/
|
||||
uint64_t rt_histogram[RANGE_TREE_HISTOGRAM_SIZE];
|
||||
kmutex_t *rt_lock; /* pointer to lock that protects map */
|
||||
} range_tree_t;
|
||||
|
||||
typedef struct range_seg {
|
||||
avl_node_t rs_node; /* AVL node */
|
||||
avl_node_t rs_pp_node; /* AVL picker-private node */
|
||||
uint64_t rs_start; /* starting offset of this segment */
|
||||
uint64_t rs_end; /* ending offset (non-inclusive) */
|
||||
} range_seg_t;
|
||||
|
||||
struct range_tree_ops {
|
||||
void (*rtop_create)(range_tree_t *rt, void *arg);
|
||||
void (*rtop_destroy)(range_tree_t *rt, void *arg);
|
||||
void (*rtop_add)(range_tree_t *rt, range_seg_t *rs, void *arg);
|
||||
void (*rtop_remove)(range_tree_t *rt, range_seg_t *rs, void *arg);
|
||||
void (*rtop_vacate)(range_tree_t *rt, void *arg);
|
||||
};
|
||||
|
||||
typedef void range_tree_func_t(void *arg, uint64_t start, uint64_t size);
|
||||
|
||||
void range_tree_init(void);
|
||||
void range_tree_fini(void);
|
||||
range_tree_t *range_tree_create(range_tree_ops_t *ops, void *arg, kmutex_t *lp);
|
||||
void range_tree_destroy(range_tree_t *rt);
|
||||
boolean_t range_tree_contains(range_tree_t *rt, uint64_t start, uint64_t size);
|
||||
uint64_t range_tree_space(range_tree_t *rt);
|
||||
void range_tree_verify(range_tree_t *rt, uint64_t start, uint64_t size);
|
||||
void range_tree_swap(range_tree_t **rtsrc, range_tree_t **rtdst);
|
||||
void range_tree_stat_verify(range_tree_t *rt);
|
||||
|
||||
void range_tree_add(void *arg, uint64_t start, uint64_t size);
|
||||
void range_tree_remove(void *arg, uint64_t start, uint64_t size);
|
||||
|
||||
void range_tree_vacate(range_tree_t *rt, range_tree_func_t *func, void *arg);
|
||||
void range_tree_walk(range_tree_t *rt, range_tree_func_t *func, void *arg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _SYS_RANGE_TREE_H */
|
Loading…
Reference in New Issue
Block a user