Merge branch 'kstat'
This branch updates several of the zfs kstats to take advantage of the improved raw kstat functionality. In addition, two new kstats and a script called dbufstat.py are introduced. Updated+New Kstats * dbufs - Stats for all dbufs in the dbuf_hash * <pool>/txgs - Stats for the last N txgs synced to disk * <pool>/reads - Stats for rhe last N reads issues by the ARC * <pool>/dmu_tx_assign - Histogram of tx assign times Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
This commit is contained in:
commit
8eaf9f3543
|
@ -1,2 +1,2 @@
|
||||||
SUBDIRS = zfs zpool zdb zhack zinject zstreamdump ztest zpios
|
SUBDIRS = zfs zpool zdb zhack zinject zstreamdump ztest zpios
|
||||||
SUBDIRS += mount_zfs fsck_zfs zvol_id vdev_id arcstat
|
SUBDIRS += mount_zfs fsck_zfs zvol_id vdev_id arcstat dbufstat
|
||||||
|
|
|
@ -106,7 +106,7 @@ opfile = None
|
||||||
sep = " " # Default separator is 2 spaces
|
sep = " " # Default separator is 2 spaces
|
||||||
version = "0.4"
|
version = "0.4"
|
||||||
l2exist = False
|
l2exist = False
|
||||||
cmd = ("Usage: arcstat [-hvx] [-f fields] [-o file] [-s string] [interval "
|
cmd = ("Usage: arcstat.py [-hvx] [-f fields] [-o file] [-s string] [interval "
|
||||||
"[count]]\n")
|
"[count]]\n")
|
||||||
cur = {}
|
cur = {}
|
||||||
d = {}
|
d = {}
|
||||||
|
@ -136,10 +136,10 @@ def usage():
|
||||||
sys.stderr.write("\t -s : Override default field separator with custom "
|
sys.stderr.write("\t -s : Override default field separator with custom "
|
||||||
"character or string\n")
|
"character or string\n")
|
||||||
sys.stderr.write("\nExamples:\n")
|
sys.stderr.write("\nExamples:\n")
|
||||||
sys.stderr.write("\tarcstat -o /tmp/a.log 2 10\n")
|
sys.stderr.write("\tarcstat.py -o /tmp/a.log 2 10\n")
|
||||||
sys.stderr.write("\tarcstat -s \",\" -o /tmp/a.log 2 10\n")
|
sys.stderr.write("\tarcstat.py -s \",\" -o /tmp/a.log 2 10\n")
|
||||||
sys.stderr.write("\tarcstat -v\n")
|
sys.stderr.write("\tarcstat.py -v\n")
|
||||||
sys.stderr.write("\tarcstat -f time,hit%,dh%,ph%,mh% 1\n")
|
sys.stderr.write("\tarcstat.py -f time,hit%,dh%,ph%,mh% 1\n")
|
||||||
sys.stderr.write("\n")
|
sys.stderr.write("\n")
|
||||||
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
bin_SCRIPTS = dbufstat.py
|
||||||
|
EXTRA_DIST = $(bin_SCRIPTS)
|
|
@ -0,0 +1,535 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
#
|
||||||
|
# Print out statistics for all cached dmu buffers. This information
|
||||||
|
# is available through the dbufs kstat and may be post-processed as
|
||||||
|
# needed by the script.
|
||||||
|
#
|
||||||
|
# CDDL HEADER START
|
||||||
|
#
|
||||||
|
# The contents of this file are subject to the terms of the
|
||||||
|
# Common Development and Distribution License, Version 1.0 only
|
||||||
|
# (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 (C) 2013 Lawrence Livermore National Security, LLC.
|
||||||
|
# Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
|
||||||
|
#
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import getopt
|
||||||
|
import errno
|
||||||
|
|
||||||
|
bhdr = ["pool", "objset", "object", "level", "blkid", "offset", "dbsize"]
|
||||||
|
bxhdr = ["pool", "objset", "object", "level", "blkid", "offset", "dbsize",
|
||||||
|
"meta", "state", "dbholds", "list", "atype", "index", "flags", "count",
|
||||||
|
"asize", "access", "mru", "gmru", "mfu", "gmfu", "l2", "l2_dattr",
|
||||||
|
"l2_asize", "l2_comp", "aholds", "dtype", "btype", "data_bs", "meta_bs",
|
||||||
|
"bsize", "lvls", "dholds", "blocks", "dsize"]
|
||||||
|
bincompat = ["cached", "direct", "indirect", "bonus", "spill"]
|
||||||
|
|
||||||
|
dhdr = ["pool", "objset", "object", "dtype", "cached"]
|
||||||
|
dxhdr = ["pool", "objset", "object", "dtype", "btype", "data_bs", "meta_bs",
|
||||||
|
"bsize", "lvls", "dholds", "blocks", "dsize", "cached", "direct",
|
||||||
|
"indirect", "bonus", "spill"]
|
||||||
|
dincompat = ["level", "blkid", "offset", "dbsize", "meta", "state", "dbholds",
|
||||||
|
"list", "atype", "index", "flags", "count", "asize", "access", "mru",
|
||||||
|
"gmru", "mfu", "gmfu", "l2", "l2_dattr", "l2_asize", "l2_comp", "aholds"]
|
||||||
|
|
||||||
|
thdr = ["pool", "objset", "dtype", "cached"]
|
||||||
|
txhdr = ["pool", "objset", "dtype", "cached", "direct", "indirect",
|
||||||
|
"bonus", "spill"]
|
||||||
|
tincompat = ["object", "level", "blkid", "offset", "dbsize", "meta", "state",
|
||||||
|
"dbholds", "list", "atype", "index", "flags", "count", "asize", "access",
|
||||||
|
"mru", "gmru", "mfu", "gmfu", "l2", "l2_dattr", "l2_asize", "l2_comp",
|
||||||
|
"aholds", "btype", "data_bs", "meta_bs", "bsize", "lvls", "dholds",
|
||||||
|
"blocks", "dsize"]
|
||||||
|
|
||||||
|
cols = {
|
||||||
|
# hdr: [size, scale, description]
|
||||||
|
"pool": [15, -1, "pool name"],
|
||||||
|
"objset": [6, -1, "dataset identification number"],
|
||||||
|
"object": [10, -1, "object number"],
|
||||||
|
"level": [5, -1, "indirection level of buffer"],
|
||||||
|
"blkid": [8, -1, "block number of buffer"],
|
||||||
|
"offset": [12, 1024, "offset in object of buffer"],
|
||||||
|
"dbsize": [7, 1024, "size of buffer"],
|
||||||
|
"meta": [4, -1, "is this buffer metadata?"],
|
||||||
|
"state": [5, -1, "state of buffer (read, cached, etc)"],
|
||||||
|
"dbholds": [7, 1000, "number of holds on buffer"],
|
||||||
|
"list": [4, -1, "which ARC list contains this buffer"],
|
||||||
|
"atype": [7, -1, "ARC header type (data or metadata)"],
|
||||||
|
"index": [5, -1, "buffer's index into its ARC list"],
|
||||||
|
"flags": [8, -1, "ARC read flags"],
|
||||||
|
"count": [5, -1, "ARC data count"],
|
||||||
|
"asize": [7, 1024, "size of this ARC buffer"],
|
||||||
|
"access": [10, -1, "time this ARC buffer was last accessed"],
|
||||||
|
"mru": [5, 1000, "hits while on the ARC's MRU list"],
|
||||||
|
"gmru": [5, 1000, "hits while on the ARC's MRU ghost list"],
|
||||||
|
"mfu": [5, 1000, "hits while on the ARC's MFU list"],
|
||||||
|
"gmfu": [5, 1000, "hits while on the ARC's MFU ghost list"],
|
||||||
|
"l2": [5, 1000, "hits while on the L2ARC"],
|
||||||
|
"l2_dattr": [8, -1, "L2ARC disk address/offset"],
|
||||||
|
"l2_asize": [8, 1024, "L2ARC alloc'd size (depending on compression)"],
|
||||||
|
"l2_comp": [21, -1, "L2ARC compression algorithm for buffer"],
|
||||||
|
"aholds": [6, 1000, "number of holds on this ARC buffer"],
|
||||||
|
"dtype": [27, -1, "dnode type"],
|
||||||
|
"btype": [27, -1, "bonus buffer type"],
|
||||||
|
"data_bs": [7, 1024, "data block size"],
|
||||||
|
"meta_bs": [7, 1024, "metadata block size"],
|
||||||
|
"bsize": [6, 1024, "bonus buffer size"],
|
||||||
|
"lvls": [6, -1, "number of indirection levels"],
|
||||||
|
"dholds": [6, 1000, "number of holds on dnode"],
|
||||||
|
"blocks": [8, 1000, "number of allocated blocks"],
|
||||||
|
"dsize": [12, 1024, "size of dnode"],
|
||||||
|
"cached": [6, 1024, "bytes cached for all blocks"],
|
||||||
|
"direct": [6, 1024, "bytes cached for direct blocks"],
|
||||||
|
"indirect": [8, 1024, "bytes cached for indirect blocks"],
|
||||||
|
"bonus": [5, 1024, "bytes cached for bonus buffer"],
|
||||||
|
"spill": [5, 1024, "bytes cached for spill block"],
|
||||||
|
}
|
||||||
|
|
||||||
|
hdr = None
|
||||||
|
xhdr = None
|
||||||
|
sep = " " # Default separator is 2 spaces
|
||||||
|
cmd = ("Usage: dbufstat.py [-bdhrtvx] [-i file] [-f fields] [-o file] "
|
||||||
|
"[-s string]\n")
|
||||||
|
raw = 0
|
||||||
|
|
||||||
|
def print_incompat_helper(incompat):
|
||||||
|
cnt = 0
|
||||||
|
for key in sorted(incompat):
|
||||||
|
if cnt is 0:
|
||||||
|
sys.stderr.write("\t")
|
||||||
|
elif cnt > 8:
|
||||||
|
sys.stderr.write(",\n\t")
|
||||||
|
cnt = 0
|
||||||
|
else:
|
||||||
|
sys.stderr.write(", ")
|
||||||
|
|
||||||
|
sys.stderr.write("%s" % key)
|
||||||
|
cnt += 1
|
||||||
|
|
||||||
|
sys.stderr.write("\n\n")
|
||||||
|
|
||||||
|
def detailed_usage():
|
||||||
|
sys.stderr.write("%s\n" % cmd)
|
||||||
|
|
||||||
|
sys.stderr.write("Field definitions incompatible with '-b' option:\n")
|
||||||
|
print_incompat_helper(bincompat)
|
||||||
|
|
||||||
|
sys.stderr.write("Field definitions incompatible with '-d' option:\n")
|
||||||
|
print_incompat_helper(dincompat)
|
||||||
|
|
||||||
|
sys.stderr.write("Field definitions incompatible with '-t' option:\n")
|
||||||
|
print_incompat_helper(tincompat)
|
||||||
|
|
||||||
|
sys.stderr.write("Field definitions are as follows:\n")
|
||||||
|
for key in sorted(cols.keys()):
|
||||||
|
sys.stderr.write("%11s : %s\n" % (key, cols[key][2]))
|
||||||
|
sys.stderr.write("\n")
|
||||||
|
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def usage():
|
||||||
|
sys.stderr.write("%s\n" % cmd)
|
||||||
|
sys.stderr.write("\t -b : Print table of information for each dbuf\n")
|
||||||
|
sys.stderr.write("\t -d : Print table of information for each dnode\n")
|
||||||
|
sys.stderr.write("\t -h : Print this help message\n")
|
||||||
|
sys.stderr.write("\t -r : Print raw values\n")
|
||||||
|
sys.stderr.write("\t -t : Print table of information for each dnode type\n")
|
||||||
|
sys.stderr.write("\t -v : List all possible field headers and definitions"
|
||||||
|
"\n")
|
||||||
|
sys.stderr.write("\t -x : Print extended stats\n")
|
||||||
|
sys.stderr.write("\t -i : Redirect input from the specified file\n")
|
||||||
|
sys.stderr.write("\t -f : Specify specific fields to print (see -v)\n")
|
||||||
|
sys.stderr.write("\t -o : Redirect output to the specified file\n")
|
||||||
|
sys.stderr.write("\t -s : Override default field separator with custom "
|
||||||
|
"character or string\n")
|
||||||
|
sys.stderr.write("\nExamples:\n")
|
||||||
|
sys.stderr.write("\tdbufstat.py -d -o /tmp/d.log\n")
|
||||||
|
sys.stderr.write("\tdbufstat.py -t -s \",\" -o /tmp/t.log\n")
|
||||||
|
sys.stderr.write("\tdbufstat.py -v\n")
|
||||||
|
sys.stderr.write("\tdbufstat.py -d -f pool,object,objset,dsize,cached\n")
|
||||||
|
sys.stderr.write("\n")
|
||||||
|
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def prettynum(sz, scale, num=0):
|
||||||
|
global raw
|
||||||
|
|
||||||
|
suffix = [' ', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']
|
||||||
|
index = 0
|
||||||
|
save = 0
|
||||||
|
|
||||||
|
if raw or scale == -1:
|
||||||
|
return "%*s" % (sz, num)
|
||||||
|
|
||||||
|
# Rounding error, return 0
|
||||||
|
elif num > 0 and num < 1:
|
||||||
|
num = 0
|
||||||
|
|
||||||
|
while num > scale and index < 5:
|
||||||
|
save = num
|
||||||
|
num = num / scale
|
||||||
|
index += 1
|
||||||
|
|
||||||
|
if index == 0:
|
||||||
|
return "%*d" % (sz, num)
|
||||||
|
|
||||||
|
if (save / scale) < 10:
|
||||||
|
return "%*.1f%s" % (sz - 1, num, suffix[index])
|
||||||
|
else:
|
||||||
|
return "%*d%s" % (sz - 1, num, suffix[index])
|
||||||
|
|
||||||
|
def print_values(v):
|
||||||
|
global hdr
|
||||||
|
global sep
|
||||||
|
|
||||||
|
try:
|
||||||
|
for col in hdr:
|
||||||
|
sys.stdout.write("%s%s" % (
|
||||||
|
prettynum(cols[col][0], cols[col][1], v[col]), sep))
|
||||||
|
sys.stdout.write("\n")
|
||||||
|
except IOError as e:
|
||||||
|
if e.errno == errno.EPIPE:
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def print_header():
|
||||||
|
global hdr
|
||||||
|
global sep
|
||||||
|
|
||||||
|
try:
|
||||||
|
for col in hdr:
|
||||||
|
sys.stdout.write("%*s%s" % (cols[col][0], col, sep))
|
||||||
|
sys.stdout.write("\n")
|
||||||
|
except IOError as e:
|
||||||
|
if e.errno == errno.EPIPE:
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def get_typestring(t):
|
||||||
|
type_strings = ["DMU_OT_NONE",
|
||||||
|
# general:
|
||||||
|
"DMU_OT_OBJECT_DIRECTORY", "DMU_OT_OBJECT_ARRAY",
|
||||||
|
"DMU_OT_PACKED_NVLIST", "DMU_OT_PACKED_NVLIST_SIZE",
|
||||||
|
"DMU_OT_BPOBJ", "DMU_OT_BPOBJ_HDR",
|
||||||
|
# spa:
|
||||||
|
"DMU_OT_SPACE_MAP_HEADER", "DMU_OT_SPACE_MAP",
|
||||||
|
# zil:
|
||||||
|
"DMU_OT_INTENT_LOG",
|
||||||
|
# dmu:
|
||||||
|
"DMU_OT_DNODE", "DMU_OT_OBJSET",
|
||||||
|
# dsl:
|
||||||
|
"DMU_OT_DSL_DIR", "DMU_OT_DSL_DIR_CHILD_MAP",
|
||||||
|
"DMU_OT_DSL_DS_SNAP_MAP", "DMU_OT_DSL_PROPS",
|
||||||
|
"DMU_OT_DSL_DATASET",
|
||||||
|
# zpl:
|
||||||
|
"DMU_OT_ZNODE", "DMU_OT_OLDACL", "DMU_OT_PLAIN_FILE_CONTENTS",
|
||||||
|
"DMU_OT_DIRECTORY_CONTENTS", "DMU_OT_MASTER_NODE",
|
||||||
|
"DMU_OT_UNLINKED_SET",
|
||||||
|
# zvol:
|
||||||
|
"DMU_OT_ZVOL", "DMU_OT_ZVOL_PROP",
|
||||||
|
# other; for testing only!
|
||||||
|
"DMU_OT_PLAIN_OTHER", "DMU_OT_UINT64_OTHER", "DMU_OT_ZAP_OTHER",
|
||||||
|
# new object types:
|
||||||
|
"DMU_OT_ERROR_LOG", "DMU_OT_SPA_HISTORY",
|
||||||
|
"DMU_OT_SPA_HISTORY_OFFSETS", "DMU_OT_POOL_PROPS",
|
||||||
|
"DMU_OT_DSL_PERMS", "DMU_OT_ACL", "DMU_OT_SYSACL",
|
||||||
|
"DMU_OT_FUID", "DMU_OT_FUID_SIZE", "DMU_OT_NEXT_CLONES",
|
||||||
|
"DMU_OT_SCAN_QUEUE", "DMU_OT_USERGROUP_USED",
|
||||||
|
"DMU_OT_USERGROUP_QUOTA", "DMU_OT_USERREFS", "DMU_OT_DDT_ZAP",
|
||||||
|
"DMU_OT_DDT_STATS", "DMU_OT_SA", "DMU_OT_SA_MASTER_NODE",
|
||||||
|
"DMU_OT_SA_ATTR_REGISTRATION", "DMU_OT_SA_ATTR_LAYOUTS",
|
||||||
|
"DMU_OT_SCAN_XLATE", "DMU_OT_DEDUP", "DMU_OT_DEADLIST",
|
||||||
|
"DMU_OT_DEADLIST_HDR", "DMU_OT_DSL_CLONES",
|
||||||
|
"DMU_OT_BPOBJ_SUBOBJ"]
|
||||||
|
|
||||||
|
# If "-rr" option is used, don't convert to string representation
|
||||||
|
if raw > 1:
|
||||||
|
return "%i" % t
|
||||||
|
|
||||||
|
try:
|
||||||
|
return type_strings[t];
|
||||||
|
except IndexError:
|
||||||
|
return "%i" % t
|
||||||
|
|
||||||
|
def get_compstring(c):
|
||||||
|
comp_strings = ["ZIO_COMPRESS_INHERIT", "ZIO_COMPRESS_ON",
|
||||||
|
"ZIO_COMPRESS_OFF", "ZIO_COMPRESS_LZJB",
|
||||||
|
"ZIO_COMPRESS_EMPTY", "ZIO_COMPRESS_GZIP_1",
|
||||||
|
"ZIO_COMPRESS_GZIP_2", "ZIO_COMPRESS_GZIP_3",
|
||||||
|
"ZIO_COMPRESS_GZIP_4", "ZIO_COMPRESS_GZIP_5",
|
||||||
|
"ZIO_COMPRESS_GZIP_6", "ZIO_COMPRESS_GZIP_7",
|
||||||
|
"ZIO_COMPRESS_GZIP_8", "ZIO_COMPRESS_GZIP_9",
|
||||||
|
"ZIO_COMPRESS_ZLE", "ZIO_COMPRESS_LZ4",
|
||||||
|
"ZIO_COMPRESS_FUNCTION"]
|
||||||
|
|
||||||
|
# If "-rr" option is used, don't convert to string representation
|
||||||
|
if raw > 1:
|
||||||
|
return "%i" % c
|
||||||
|
|
||||||
|
try:
|
||||||
|
return comp_strings[c];
|
||||||
|
except IndexError:
|
||||||
|
return "%i" % c
|
||||||
|
|
||||||
|
def parse_line(line, labels):
|
||||||
|
global hdr
|
||||||
|
|
||||||
|
new = dict()
|
||||||
|
val = None
|
||||||
|
for col in hdr:
|
||||||
|
# These are "special" fields computed in the update_dict
|
||||||
|
# function, prevent KeyError exception on labels[col] for these.
|
||||||
|
if col not in ['bonus', 'cached', 'direct', 'indirect', 'spill']:
|
||||||
|
val = line[labels[col]]
|
||||||
|
|
||||||
|
if col in ['pool', 'flags']:
|
||||||
|
new[col] = str(val)
|
||||||
|
elif col in ['dtype', 'btype']:
|
||||||
|
new[col] = get_typestring(int(val))
|
||||||
|
elif col in ['l2_comp']:
|
||||||
|
new[col] = get_compstring(int(val))
|
||||||
|
else:
|
||||||
|
new[col] = int(val)
|
||||||
|
|
||||||
|
return new
|
||||||
|
|
||||||
|
def update_dict(d, k, line, labels):
|
||||||
|
pool = line[labels['pool']]
|
||||||
|
objset = line[labels['objset']]
|
||||||
|
key = line[labels[k]]
|
||||||
|
|
||||||
|
dbsize = int(line[labels['dbsize']])
|
||||||
|
blkid = int(line[labels['blkid']])
|
||||||
|
level = int(line[labels['level']])
|
||||||
|
|
||||||
|
if pool not in d:
|
||||||
|
d[pool] = dict()
|
||||||
|
|
||||||
|
if objset not in d[pool]:
|
||||||
|
d[pool][objset] = dict()
|
||||||
|
|
||||||
|
if key not in d[pool][objset]:
|
||||||
|
d[pool][objset][key] = parse_line(line, labels)
|
||||||
|
d[pool][objset][key]['bonus'] = 0
|
||||||
|
d[pool][objset][key]['cached'] = 0
|
||||||
|
d[pool][objset][key]['direct'] = 0
|
||||||
|
d[pool][objset][key]['indirect'] = 0
|
||||||
|
d[pool][objset][key]['spill'] = 0
|
||||||
|
|
||||||
|
d[pool][objset][key]['cached'] += dbsize
|
||||||
|
|
||||||
|
if blkid == -1:
|
||||||
|
d[pool][objset][key]['bonus'] += dbsize
|
||||||
|
elif blkid == -2:
|
||||||
|
d[pool][objset][key]['spill'] += dbsize
|
||||||
|
else:
|
||||||
|
if level == 0:
|
||||||
|
d[pool][objset][key]['direct'] += dbsize
|
||||||
|
else:
|
||||||
|
d[pool][objset][key]['indirect'] += dbsize
|
||||||
|
|
||||||
|
return d
|
||||||
|
|
||||||
|
def print_dict(d):
|
||||||
|
print_header()
|
||||||
|
for pool in d.iterkeys():
|
||||||
|
for objset in d[pool].iterkeys():
|
||||||
|
for v in d[pool][objset].itervalues():
|
||||||
|
print_values(v)
|
||||||
|
|
||||||
|
def dnodes_build_dict(filehandle):
|
||||||
|
labels = dict()
|
||||||
|
dnodes = dict()
|
||||||
|
|
||||||
|
# First 3 lines are header information, skip the first two
|
||||||
|
for i in range(0, 2):
|
||||||
|
next(filehandle)
|
||||||
|
|
||||||
|
# The third line contains the labels and index locations
|
||||||
|
for i, v in enumerate(next(filehandle).split()):
|
||||||
|
labels[v] = i
|
||||||
|
|
||||||
|
# The rest of the file is buffer information
|
||||||
|
for line in filehandle:
|
||||||
|
update_dict(dnodes, 'object', line.split(), labels)
|
||||||
|
|
||||||
|
return dnodes
|
||||||
|
|
||||||
|
def types_build_dict(filehandle):
|
||||||
|
labels = dict()
|
||||||
|
types = dict()
|
||||||
|
|
||||||
|
# First 3 lines are header information, skip the first two
|
||||||
|
for i in range(0, 2):
|
||||||
|
next(filehandle)
|
||||||
|
|
||||||
|
# The third line contains the labels and index locations
|
||||||
|
for i, v in enumerate(next(filehandle).split()):
|
||||||
|
labels[v] = i
|
||||||
|
|
||||||
|
# The rest of the file is buffer information
|
||||||
|
for line in filehandle:
|
||||||
|
update_dict(types, 'dtype', line.split(), labels)
|
||||||
|
|
||||||
|
return types
|
||||||
|
|
||||||
|
def buffers_print_all(filehandle):
|
||||||
|
labels = dict()
|
||||||
|
|
||||||
|
# First 3 lines are header information, skip the first two
|
||||||
|
for i in range(0, 2):
|
||||||
|
next(filehandle)
|
||||||
|
|
||||||
|
# The third line contains the labels and index locations
|
||||||
|
for i, v in enumerate(next(filehandle).split()):
|
||||||
|
labels[v] = i
|
||||||
|
|
||||||
|
print_header()
|
||||||
|
|
||||||
|
# The rest of the file is buffer information
|
||||||
|
for line in filehandle:
|
||||||
|
print_values(parse_line(line.split(), labels))
|
||||||
|
|
||||||
|
def main():
|
||||||
|
global hdr
|
||||||
|
global sep
|
||||||
|
global raw
|
||||||
|
|
||||||
|
desired_cols = None
|
||||||
|
bflag = False
|
||||||
|
dflag = False
|
||||||
|
hflag = False
|
||||||
|
ifile = None
|
||||||
|
ofile = None
|
||||||
|
tflag = False
|
||||||
|
vflag = False
|
||||||
|
xflag = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
opts, args = getopt.getopt(
|
||||||
|
sys.argv[1:],
|
||||||
|
"bdf:hi:o:rs:tvx",
|
||||||
|
[
|
||||||
|
"buffers",
|
||||||
|
"dnodes",
|
||||||
|
"columns",
|
||||||
|
"help",
|
||||||
|
"infile",
|
||||||
|
"outfile",
|
||||||
|
"seperator",
|
||||||
|
"types",
|
||||||
|
"verbose",
|
||||||
|
"extended"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
except getopt.error:
|
||||||
|
usage()
|
||||||
|
|
||||||
|
for opt, arg in opts:
|
||||||
|
if opt in ('-b', '--buffers'):
|
||||||
|
bflag = True
|
||||||
|
if opt in ('-d', '--dnodes'):
|
||||||
|
dflag = True
|
||||||
|
if opt in ('-f', '--columns'):
|
||||||
|
desired_cols = arg
|
||||||
|
if opt in ('-h', '--help'):
|
||||||
|
hflag = True
|
||||||
|
if opt in ('-i', '--infile'):
|
||||||
|
ifile = arg
|
||||||
|
if opt in ('-o', '--outfile'):
|
||||||
|
ofile = arg
|
||||||
|
if opt in ('-r', '--raw'):
|
||||||
|
raw += 1
|
||||||
|
if opt in ('-s', '--seperator'):
|
||||||
|
sep = arg
|
||||||
|
if opt in ('-t', '--types'):
|
||||||
|
tflag = True
|
||||||
|
if opt in ('-v', '--verbose'):
|
||||||
|
vflag = True
|
||||||
|
if opt in ('-x', '--extended'):
|
||||||
|
xflag = True
|
||||||
|
|
||||||
|
if hflag or (xflag and desired_cols):
|
||||||
|
usage()
|
||||||
|
|
||||||
|
if vflag:
|
||||||
|
detailed_usage()
|
||||||
|
|
||||||
|
# Ensure at most only one of b, d, or t flags are set
|
||||||
|
if (bflag and dflag) or (bflag and tflag) or (dflag and tflag):
|
||||||
|
usage()
|
||||||
|
|
||||||
|
if bflag:
|
||||||
|
hdr = bxhdr if xflag else bhdr
|
||||||
|
elif tflag:
|
||||||
|
hdr = txhdr if xflag else thdr
|
||||||
|
else: # Even if dflag is False, it's the default if none set
|
||||||
|
dflag = True
|
||||||
|
hdr = dxhdr if xflag else dhdr
|
||||||
|
|
||||||
|
if desired_cols:
|
||||||
|
hdr = desired_cols.split(",")
|
||||||
|
|
||||||
|
invalid = []
|
||||||
|
incompat = []
|
||||||
|
for ele in hdr:
|
||||||
|
if ele not in cols:
|
||||||
|
invalid.append(ele)
|
||||||
|
elif ((bflag and bincompat and ele in bincompat) or
|
||||||
|
(dflag and dincompat and ele in dincompat) or
|
||||||
|
(tflag and tincompat and ele in tincompat)):
|
||||||
|
incompat.append(ele)
|
||||||
|
|
||||||
|
if len(invalid) > 0:
|
||||||
|
sys.stderr.write("Invalid column definition! -- %s\n" % invalid)
|
||||||
|
usage()
|
||||||
|
|
||||||
|
if len(incompat) > 0:
|
||||||
|
sys.stderr.write("Incompatible field specified! -- %s\n" % incompat)
|
||||||
|
usage()
|
||||||
|
|
||||||
|
if ofile:
|
||||||
|
try:
|
||||||
|
tmp = open(ofile, "w")
|
||||||
|
sys.stdout = tmp
|
||||||
|
|
||||||
|
except:
|
||||||
|
sys.stderr.write("Cannot open %s for writing\n", ofile)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if not ifile:
|
||||||
|
ifile = '/proc/spl/kstat/zfs/dbufs'
|
||||||
|
|
||||||
|
if ifile is not "-":
|
||||||
|
try:
|
||||||
|
tmp = open(ifile, "r")
|
||||||
|
sys.stdin = tmp
|
||||||
|
except:
|
||||||
|
sys.stderr.write("Cannot open %s for reading\n" % ifile)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if bflag:
|
||||||
|
buffers_print_all(sys.stdin)
|
||||||
|
|
||||||
|
if dflag:
|
||||||
|
print_dict(dnodes_build_dict(sys.stdin))
|
||||||
|
|
||||||
|
if tflag:
|
||||||
|
print_dict(types_build_dict(sys.stdin))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
|
@ -104,6 +104,7 @@ AC_CONFIG_FILES([
|
||||||
cmd/zvol_id/Makefile
|
cmd/zvol_id/Makefile
|
||||||
cmd/vdev_id/Makefile
|
cmd/vdev_id/Makefile
|
||||||
cmd/arcstat/Makefile
|
cmd/arcstat/Makefile
|
||||||
|
cmd/dbufstat/Makefile
|
||||||
module/Makefile
|
module/Makefile
|
||||||
module/avl/Makefile
|
module/avl/Makefile
|
||||||
module/nvpair/Makefile
|
module/nvpair/Makefile
|
||||||
|
|
|
@ -92,6 +92,36 @@ typedef enum arc_space_type {
|
||||||
ARC_SPACE_NUMTYPES
|
ARC_SPACE_NUMTYPES
|
||||||
} arc_space_type_t;
|
} arc_space_type_t;
|
||||||
|
|
||||||
|
typedef enum arc_state_type {
|
||||||
|
ARC_STATE_ANON,
|
||||||
|
ARC_STATE_MRU,
|
||||||
|
ARC_STATE_MRU_GHOST,
|
||||||
|
ARC_STATE_MFU,
|
||||||
|
ARC_STATE_MFU_GHOST,
|
||||||
|
ARC_STATE_L2C_ONLY,
|
||||||
|
ARC_STATE_NUMTYPES
|
||||||
|
} arc_state_type_t;
|
||||||
|
|
||||||
|
typedef struct arc_buf_info {
|
||||||
|
arc_state_type_t abi_state_type;
|
||||||
|
arc_buf_contents_t abi_state_contents;
|
||||||
|
uint64_t abi_state_index;
|
||||||
|
uint32_t abi_flags;
|
||||||
|
uint32_t abi_datacnt;
|
||||||
|
uint64_t abi_size;
|
||||||
|
uint64_t abi_spa;
|
||||||
|
uint64_t abi_access;
|
||||||
|
uint32_t abi_mru_hits;
|
||||||
|
uint32_t abi_mru_ghost_hits;
|
||||||
|
uint32_t abi_mfu_hits;
|
||||||
|
uint32_t abi_mfu_ghost_hits;
|
||||||
|
uint32_t abi_l2arc_hits;
|
||||||
|
uint32_t abi_holds;
|
||||||
|
uint64_t abi_l2arc_dattr;
|
||||||
|
uint64_t abi_l2arc_asize;
|
||||||
|
enum zio_compress abi_l2arc_compress;
|
||||||
|
} arc_buf_info_t;
|
||||||
|
|
||||||
void arc_space_consume(uint64_t space, arc_space_type_t type);
|
void arc_space_consume(uint64_t space, arc_space_type_t type);
|
||||||
void arc_space_return(uint64_t space, arc_space_type_t type);
|
void arc_space_return(uint64_t space, arc_space_type_t type);
|
||||||
arc_buf_t *arc_buf_alloc(spa_t *spa, int size, void *tag,
|
arc_buf_t *arc_buf_alloc(spa_t *spa, int size, void *tag,
|
||||||
|
@ -101,6 +131,7 @@ void arc_return_buf(arc_buf_t *buf, void *tag);
|
||||||
void arc_loan_inuse_buf(arc_buf_t *buf, void *tag);
|
void arc_loan_inuse_buf(arc_buf_t *buf, void *tag);
|
||||||
void arc_buf_add_ref(arc_buf_t *buf, void *tag);
|
void arc_buf_add_ref(arc_buf_t *buf, void *tag);
|
||||||
boolean_t arc_buf_remove_ref(arc_buf_t *buf, void *tag);
|
boolean_t arc_buf_remove_ref(arc_buf_t *buf, void *tag);
|
||||||
|
void arc_buf_info(arc_buf_t *buf, arc_buf_info_t *abi, int state_index);
|
||||||
int arc_buf_size(arc_buf_t *buf);
|
int arc_buf_size(arc_buf_t *buf);
|
||||||
void arc_release(arc_buf_t *buf, void *tag);
|
void arc_release(arc_buf_t *buf, void *tag);
|
||||||
int arc_released(arc_buf_t *buf);
|
int arc_released(arc_buf_t *buf);
|
||||||
|
|
|
@ -282,6 +282,9 @@ void dbuf_free_range(struct dnode *dn, uint64_t start, uint64_t end,
|
||||||
|
|
||||||
void dbuf_new_size(dmu_buf_impl_t *db, int size, dmu_tx_t *tx);
|
void dbuf_new_size(dmu_buf_impl_t *db, int size, dmu_tx_t *tx);
|
||||||
|
|
||||||
|
void dbuf_stats_init(dbuf_hash_table_t *hash);
|
||||||
|
void dbuf_stats_destroy(void);
|
||||||
|
|
||||||
#define DB_DNODE(_db) ((_db)->db_dnode_handle->dnh_dnode)
|
#define DB_DNODE(_db) ((_db)->db_dnode_handle->dnh_dnode)
|
||||||
#define DB_DNODE_LOCK(_db) ((_db)->db_dnode_handle->dnh_zrlock)
|
#define DB_DNODE_LOCK(_db) ((_db)->db_dnode_handle->dnh_zrlock)
|
||||||
#define DB_DNODE_ENTER(_db) (zrl_add(&DB_DNODE_LOCK(_db)))
|
#define DB_DNODE_ENTER(_db) (zrl_add(&DB_DNODE_LOCK(_db)))
|
||||||
|
|
|
@ -660,6 +660,7 @@ extern const dmu_object_byteswap_info_t dmu_ot_byteswap[DMU_BSWAP_NUMFUNCS];
|
||||||
* If doi is NULL, just indicates whether the object exists.
|
* If doi is NULL, just indicates whether the object exists.
|
||||||
*/
|
*/
|
||||||
int dmu_object_info(objset_t *os, uint64_t object, dmu_object_info_t *doi);
|
int dmu_object_info(objset_t *os, uint64_t object, dmu_object_info_t *doi);
|
||||||
|
void __dmu_object_info_from_dnode(struct dnode *dn, dmu_object_info_t *doi);
|
||||||
void dmu_object_info_from_dnode(struct dnode *dn, dmu_object_info_t *doi);
|
void dmu_object_info_from_dnode(struct dnode *dn, dmu_object_info_t *doi);
|
||||||
void dmu_object_info_from_db(dmu_buf_t *db, dmu_object_info_t *doi);
|
void dmu_object_info_from_db(dmu_buf_t *db, dmu_object_info_t *doi);
|
||||||
void dmu_object_size_from_db(dmu_buf_t *db, uint32_t *blksize,
|
void dmu_object_size_from_db(dmu_buf_t *db, uint32_t *blksize,
|
||||||
|
|
|
@ -71,13 +71,6 @@ typedef struct zfs_all_blkstats {
|
||||||
zfs_blkstat_t zab_type[DN_MAX_LEVELS + 1][DMU_OT_TOTAL + 1];
|
zfs_blkstat_t zab_type[DN_MAX_LEVELS + 1][DMU_OT_TOTAL + 1];
|
||||||
} zfs_all_blkstats_t;
|
} zfs_all_blkstats_t;
|
||||||
|
|
||||||
typedef struct txg_history {
|
|
||||||
kstat_txg_t th_kstat;
|
|
||||||
vdev_stat_t th_vs1;
|
|
||||||
vdev_stat_t th_vs2;
|
|
||||||
kmutex_t th_lock;
|
|
||||||
list_node_t th_link;
|
|
||||||
} txg_history_t;
|
|
||||||
|
|
||||||
typedef struct dsl_pool {
|
typedef struct dsl_pool {
|
||||||
/* Immutable */
|
/* Immutable */
|
||||||
|
@ -89,8 +82,6 @@ typedef struct dsl_pool {
|
||||||
struct dsl_dataset *dp_origin_snap;
|
struct dsl_dataset *dp_origin_snap;
|
||||||
uint64_t dp_root_dir_obj;
|
uint64_t dp_root_dir_obj;
|
||||||
struct taskq *dp_iput_taskq;
|
struct taskq *dp_iput_taskq;
|
||||||
kstat_t *dp_txg_kstat;
|
|
||||||
kstat_t *dp_tx_assign_kstat;
|
|
||||||
|
|
||||||
/* No lock needed - sync context only */
|
/* No lock needed - sync context only */
|
||||||
blkptr_t dp_meta_rootbp;
|
blkptr_t dp_meta_rootbp;
|
||||||
|
@ -111,11 +102,6 @@ typedef struct dsl_pool {
|
||||||
uint64_t dp_mos_used_delta;
|
uint64_t dp_mos_used_delta;
|
||||||
uint64_t dp_mos_compressed_delta;
|
uint64_t dp_mos_compressed_delta;
|
||||||
uint64_t dp_mos_uncompressed_delta;
|
uint64_t dp_mos_uncompressed_delta;
|
||||||
uint64_t dp_txg_history_size;
|
|
||||||
list_t dp_txg_history;
|
|
||||||
uint64_t dp_tx_assign_size;
|
|
||||||
kstat_named_t *dp_tx_assign_buckets;
|
|
||||||
|
|
||||||
|
|
||||||
/* Has its own locking */
|
/* Has its own locking */
|
||||||
tx_state_t dp_tx;
|
tx_state_t dp_tx;
|
||||||
|
@ -171,12 +157,6 @@ int dsl_pool_open_special_dir(dsl_pool_t *dp, const char *name, dsl_dir_t **);
|
||||||
int dsl_pool_hold(const char *name, void *tag, dsl_pool_t **dp);
|
int dsl_pool_hold(const char *name, void *tag, dsl_pool_t **dp);
|
||||||
void dsl_pool_rele(dsl_pool_t *dp, void *tag);
|
void dsl_pool_rele(dsl_pool_t *dp, void *tag);
|
||||||
|
|
||||||
void dsl_pool_tx_assign_add_usecs(dsl_pool_t *dp, uint64_t usecs);
|
|
||||||
|
|
||||||
txg_history_t *dsl_pool_txg_history_add(dsl_pool_t *dp, uint64_t txg);
|
|
||||||
txg_history_t *dsl_pool_txg_history_get(dsl_pool_t *dp, uint64_t txg);
|
|
||||||
void dsl_pool_txg_history_put(txg_history_t *th);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -51,6 +51,8 @@ typedef struct zilog zilog_t;
|
||||||
typedef struct spa_aux_vdev spa_aux_vdev_t;
|
typedef struct spa_aux_vdev spa_aux_vdev_t;
|
||||||
typedef struct ddt ddt_t;
|
typedef struct ddt ddt_t;
|
||||||
typedef struct ddt_entry ddt_entry_t;
|
typedef struct ddt_entry ddt_entry_t;
|
||||||
|
typedef struct zbookmark zbookmark_t;
|
||||||
|
|
||||||
struct dsl_pool;
|
struct dsl_pool;
|
||||||
struct dsl_dataset;
|
struct dsl_dataset;
|
||||||
|
|
||||||
|
@ -534,6 +536,41 @@ extern boolean_t spa_refcount_zero(spa_t *spa);
|
||||||
#define SCL_ALL ((1 << SCL_LOCKS) - 1)
|
#define SCL_ALL ((1 << SCL_LOCKS) - 1)
|
||||||
#define SCL_STATE_ALL (SCL_STATE | SCL_L2ARC | SCL_ZIO)
|
#define SCL_STATE_ALL (SCL_STATE | SCL_L2ARC | SCL_ZIO)
|
||||||
|
|
||||||
|
/* Historical pool statistics */
|
||||||
|
typedef struct spa_stats_history {
|
||||||
|
kmutex_t lock;
|
||||||
|
uint64_t count;
|
||||||
|
uint64_t size;
|
||||||
|
kstat_t *kstat;
|
||||||
|
void *private;
|
||||||
|
list_t list;
|
||||||
|
} spa_stats_history_t;
|
||||||
|
|
||||||
|
typedef struct spa_stats {
|
||||||
|
spa_stats_history_t read_history;
|
||||||
|
spa_stats_history_t txg_history;
|
||||||
|
spa_stats_history_t tx_assign_histogram;
|
||||||
|
} spa_stats_t;
|
||||||
|
|
||||||
|
typedef enum txg_state {
|
||||||
|
TXG_STATE_BIRTH = 0,
|
||||||
|
TXG_STATE_OPEN = 1,
|
||||||
|
TXG_STATE_QUIESCED = 2,
|
||||||
|
TXG_STATE_SYNCED = 3,
|
||||||
|
TXG_STATE_COMMITTED = 4,
|
||||||
|
} txg_state_t;
|
||||||
|
|
||||||
|
extern void spa_stats_init(spa_t *spa);
|
||||||
|
extern void spa_stats_destroy(spa_t *spa);
|
||||||
|
extern void spa_read_history_add(spa_t *spa, const zbookmark_t *zb,
|
||||||
|
uint32_t aflags);
|
||||||
|
extern void spa_txg_history_add(spa_t *spa, uint64_t txg);
|
||||||
|
extern int spa_txg_history_set(spa_t *spa, uint64_t txg,
|
||||||
|
txg_state_t completed_state, hrtime_t completed_time);
|
||||||
|
extern int spa_txg_history_set_io(spa_t *spa, uint64_t txg, uint64_t nread,
|
||||||
|
uint64_t nwritten, uint64_t reads, uint64_t writes, uint64_t nreserved);
|
||||||
|
extern void spa_tx_assign_add_nsecs(spa_t *spa, uint64_t nsecs);
|
||||||
|
|
||||||
/* Pool configuration locks */
|
/* Pool configuration locks */
|
||||||
extern int spa_config_tryenter(spa_t *spa, int locks, void *tag, krw_t rw);
|
extern int spa_config_tryenter(spa_t *spa, int locks, void *tag, krw_t rw);
|
||||||
extern void spa_config_enter(spa_t *spa, int locks, void *tag, krw_t rw);
|
extern void spa_config_enter(spa_t *spa, int locks, void *tag, krw_t rw);
|
||||||
|
|
|
@ -236,6 +236,8 @@ struct spa {
|
||||||
uint64_t spa_deadman_calls; /* number of deadman calls */
|
uint64_t spa_deadman_calls; /* number of deadman calls */
|
||||||
uint64_t spa_sync_starttime; /* starting time fo spa_sync */
|
uint64_t spa_sync_starttime; /* starting time fo spa_sync */
|
||||||
uint64_t spa_deadman_synctime; /* deadman expiration timer */
|
uint64_t spa_deadman_synctime; /* deadman expiration timer */
|
||||||
|
spa_stats_t spa_stats; /* assorted spa statistics */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* spa_refcnt & spa_config_lock must be the last elements
|
* spa_refcnt & spa_config_lock must be the last elements
|
||||||
* because refcount_t changes size based on compilation options.
|
* because refcount_t changes size based on compilation options.
|
||||||
|
|
|
@ -210,6 +210,7 @@ typedef struct kthread {
|
||||||
} kthread_t;
|
} kthread_t;
|
||||||
|
|
||||||
#define curthread zk_thread_current()
|
#define curthread zk_thread_current()
|
||||||
|
#define getcomm() "unknown"
|
||||||
#define thread_exit zk_thread_exit
|
#define thread_exit zk_thread_exit
|
||||||
#define thread_create(stk, stksize, func, arg, len, pp, state, pri) \
|
#define thread_create(stk, stksize, func, arg, len, pp, state, pri) \
|
||||||
zk_thread_create(stk, stksize, (thread_func_t)func, arg, \
|
zk_thread_create(stk, stksize, (thread_func_t)func, arg, \
|
||||||
|
@ -347,6 +348,10 @@ extern kstat_t *kstat_create(char *, int,
|
||||||
char *, char *, uchar_t, ulong_t, uchar_t);
|
char *, char *, uchar_t, ulong_t, uchar_t);
|
||||||
extern void kstat_install(kstat_t *);
|
extern void kstat_install(kstat_t *);
|
||||||
extern void kstat_delete(kstat_t *);
|
extern void kstat_delete(kstat_t *);
|
||||||
|
extern void kstat_set_raw_ops(kstat_t *ksp,
|
||||||
|
int (*headers)(char *buf, size_t size),
|
||||||
|
int (*data)(char *buf, size_t size, void *data),
|
||||||
|
void *(*addr)(kstat_t *ksp, loff_t index));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Kernel memory
|
* Kernel memory
|
||||||
|
|
|
@ -256,12 +256,13 @@ extern char *zio_type_name[ZIO_TYPES];
|
||||||
* Therefore it must not change size or alignment between 32/64 bit
|
* Therefore it must not change size or alignment between 32/64 bit
|
||||||
* compilation options.
|
* compilation options.
|
||||||
*/
|
*/
|
||||||
typedef struct zbookmark {
|
struct zbookmark {
|
||||||
uint64_t zb_objset;
|
uint64_t zb_objset;
|
||||||
uint64_t zb_object;
|
uint64_t zb_object;
|
||||||
int64_t zb_level;
|
int64_t zb_level;
|
||||||
uint64_t zb_blkid;
|
uint64_t zb_blkid;
|
||||||
} zbookmark_t;
|
char * zb_func;
|
||||||
|
};
|
||||||
|
|
||||||
#define SET_BOOKMARK(zb, objset, object, level, blkid) \
|
#define SET_BOOKMARK(zb, objset, object, level, blkid) \
|
||||||
{ \
|
{ \
|
||||||
|
@ -269,6 +270,7 @@ typedef struct zbookmark {
|
||||||
(zb)->zb_object = object; \
|
(zb)->zb_object = object; \
|
||||||
(zb)->zb_level = level; \
|
(zb)->zb_level = level; \
|
||||||
(zb)->zb_blkid = blkid; \
|
(zb)->zb_blkid = blkid; \
|
||||||
|
(zb)->zb_func = FTAG; \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ZB_DESTROYED_OBJSET (-1ULL)
|
#define ZB_DESTROYED_OBJSET (-1ULL)
|
||||||
|
|
|
@ -228,10 +228,8 @@ typedef struct kstat32 {
|
||||||
/* ks_ndata == 1 */
|
/* ks_ndata == 1 */
|
||||||
#define KSTAT_TYPE_TIMER 4 /* event timer */
|
#define KSTAT_TYPE_TIMER 4 /* event timer */
|
||||||
/* ks_ndata >= 1 */
|
/* ks_ndata >= 1 */
|
||||||
#define KSTAT_TYPE_TXG 5 /* txg statistics */
|
|
||||||
/* ks_ndata >= 0 */
|
|
||||||
|
|
||||||
#define KSTAT_NUM_TYPES 6
|
#define KSTAT_NUM_TYPES 5
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* kstat class
|
* kstat class
|
||||||
|
@ -700,29 +698,6 @@ typedef struct kstat_timer {
|
||||||
|
|
||||||
#define KSTAT_TIMER_PTR(kptr) ((kstat_timer_t *)(kptr)->ks_data)
|
#define KSTAT_TIMER_PTR(kptr) ((kstat_timer_t *)(kptr)->ks_data)
|
||||||
|
|
||||||
/*
|
|
||||||
* TXG statistics - bytes read/written and iops performed
|
|
||||||
*/
|
|
||||||
typedef enum kstat_txg_state {
|
|
||||||
TXG_STATE_OPEN = 1,
|
|
||||||
TXG_STATE_QUIESCING = 2,
|
|
||||||
TXG_STATE_SYNCING = 3,
|
|
||||||
TXG_STATE_COMMITTED = 4,
|
|
||||||
} kstat_txg_state_t;
|
|
||||||
|
|
||||||
typedef struct kstat_txg {
|
|
||||||
u_longlong_t txg; /* txg id */
|
|
||||||
kstat_txg_state_t state; /* txg state */
|
|
||||||
hrtime_t birth; /* birth time stamp */
|
|
||||||
u_longlong_t nread; /* number of bytes read */
|
|
||||||
u_longlong_t nwritten; /* number of bytes written */
|
|
||||||
uint_t reads; /* number of read operations */
|
|
||||||
uint_t writes; /* number of write operations */
|
|
||||||
hrtime_t open_time; /* open time */
|
|
||||||
hrtime_t quiesce_time; /* quiesce time */
|
|
||||||
hrtime_t sync_time; /* sync time */
|
|
||||||
} kstat_txg_t;
|
|
||||||
|
|
||||||
#if defined(_KERNEL)
|
#if defined(_KERNEL)
|
||||||
|
|
||||||
#include <sys/t_lock.h>
|
#include <sys/t_lock.h>
|
||||||
|
|
|
@ -25,6 +25,7 @@ libzpool_la_SOURCES = \
|
||||||
$(top_srcdir)/module/zfs/bpobj.c \
|
$(top_srcdir)/module/zfs/bpobj.c \
|
||||||
$(top_srcdir)/module/zfs/bptree.c \
|
$(top_srcdir)/module/zfs/bptree.c \
|
||||||
$(top_srcdir)/module/zfs/dbuf.c \
|
$(top_srcdir)/module/zfs/dbuf.c \
|
||||||
|
$(top_srcdir)/module/zfs/dbuf_stats.c \
|
||||||
$(top_srcdir)/module/zfs/ddt.c \
|
$(top_srcdir)/module/zfs/ddt.c \
|
||||||
$(top_srcdir)/module/zfs/ddt_zap.c \
|
$(top_srcdir)/module/zfs/ddt_zap.c \
|
||||||
$(top_srcdir)/module/zfs/dmu.c \
|
$(top_srcdir)/module/zfs/dmu.c \
|
||||||
|
@ -62,6 +63,7 @@ libzpool_la_SOURCES = \
|
||||||
$(top_srcdir)/module/zfs/spa_errlog.c \
|
$(top_srcdir)/module/zfs/spa_errlog.c \
|
||||||
$(top_srcdir)/module/zfs/spa_history.c \
|
$(top_srcdir)/module/zfs/spa_history.c \
|
||||||
$(top_srcdir)/module/zfs/spa_misc.c \
|
$(top_srcdir)/module/zfs/spa_misc.c \
|
||||||
|
$(top_srcdir)/module/zfs/spa_stats.c \
|
||||||
$(top_srcdir)/module/zfs/space_map.c \
|
$(top_srcdir)/module/zfs/space_map.c \
|
||||||
$(top_srcdir)/module/zfs/txg.c \
|
$(top_srcdir)/module/zfs/txg.c \
|
||||||
$(top_srcdir)/module/zfs/uberblock.c \
|
$(top_srcdir)/module/zfs/uberblock.c \
|
||||||
|
|
|
@ -240,6 +240,14 @@ void
|
||||||
kstat_delete(kstat_t *ksp)
|
kstat_delete(kstat_t *ksp)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
/*ARGSUSED*/
|
||||||
|
void
|
||||||
|
kstat_set_raw_ops(kstat_t *ksp,
|
||||||
|
int (*headers)(char *buf, size_t size),
|
||||||
|
int (*data)(char *buf, size_t size, void *data),
|
||||||
|
void *(*addr)(kstat_t *ksp, loff_t index))
|
||||||
|
{}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* =========================================================================
|
* =========================================================================
|
||||||
* mutexes
|
* mutexes
|
||||||
|
|
|
@ -8,6 +8,7 @@ $(MODULE)-objs += @top_srcdir@/module/zfs/arc.o
|
||||||
$(MODULE)-objs += @top_srcdir@/module/zfs/bplist.o
|
$(MODULE)-objs += @top_srcdir@/module/zfs/bplist.o
|
||||||
$(MODULE)-objs += @top_srcdir@/module/zfs/bpobj.o
|
$(MODULE)-objs += @top_srcdir@/module/zfs/bpobj.o
|
||||||
$(MODULE)-objs += @top_srcdir@/module/zfs/dbuf.o
|
$(MODULE)-objs += @top_srcdir@/module/zfs/dbuf.o
|
||||||
|
$(MODULE)-objs += @top_srcdir@/module/zfs/dbuf_stats.o
|
||||||
$(MODULE)-objs += @top_srcdir@/module/zfs/bptree.o
|
$(MODULE)-objs += @top_srcdir@/module/zfs/bptree.o
|
||||||
$(MODULE)-objs += @top_srcdir@/module/zfs/ddt.o
|
$(MODULE)-objs += @top_srcdir@/module/zfs/ddt.o
|
||||||
$(MODULE)-objs += @top_srcdir@/module/zfs/ddt_zap.o
|
$(MODULE)-objs += @top_srcdir@/module/zfs/ddt_zap.o
|
||||||
|
@ -44,6 +45,7 @@ $(MODULE)-objs += @top_srcdir@/module/zfs/spa_config.o
|
||||||
$(MODULE)-objs += @top_srcdir@/module/zfs/spa_errlog.o
|
$(MODULE)-objs += @top_srcdir@/module/zfs/spa_errlog.o
|
||||||
$(MODULE)-objs += @top_srcdir@/module/zfs/spa_history.o
|
$(MODULE)-objs += @top_srcdir@/module/zfs/spa_history.o
|
||||||
$(MODULE)-objs += @top_srcdir@/module/zfs/spa_misc.o
|
$(MODULE)-objs += @top_srcdir@/module/zfs/spa_misc.o
|
||||||
|
$(MODULE)-objs += @top_srcdir@/module/zfs/spa_stats.o
|
||||||
$(MODULE)-objs += @top_srcdir@/module/zfs/space_map.o
|
$(MODULE)-objs += @top_srcdir@/module/zfs/space_map.o
|
||||||
$(MODULE)-objs += @top_srcdir@/module/zfs/txg.o
|
$(MODULE)-objs += @top_srcdir@/module/zfs/txg.o
|
||||||
$(MODULE)-objs += @top_srcdir@/module/zfs/uberblock.o
|
$(MODULE)-objs += @top_srcdir@/module/zfs/uberblock.o
|
||||||
|
|
107
module/zfs/arc.c
107
module/zfs/arc.c
|
@ -232,6 +232,7 @@ typedef struct arc_state {
|
||||||
uint64_t arcs_lsize[ARC_BUFC_NUMTYPES]; /* amount of evictable data */
|
uint64_t arcs_lsize[ARC_BUFC_NUMTYPES]; /* amount of evictable data */
|
||||||
uint64_t arcs_size; /* total amount of data in this state */
|
uint64_t arcs_size; /* total amount of data in this state */
|
||||||
kmutex_t arcs_mtx;
|
kmutex_t arcs_mtx;
|
||||||
|
arc_state_type_t arcs_state;
|
||||||
} arc_state_t;
|
} arc_state_t;
|
||||||
|
|
||||||
/* The 6 states: */
|
/* The 6 states: */
|
||||||
|
@ -534,6 +535,11 @@ struct arc_buf_hdr {
|
||||||
|
|
||||||
/* updated atomically */
|
/* updated atomically */
|
||||||
clock_t b_arc_access;
|
clock_t b_arc_access;
|
||||||
|
uint32_t b_mru_hits;
|
||||||
|
uint32_t b_mru_ghost_hits;
|
||||||
|
uint32_t b_mfu_hits;
|
||||||
|
uint32_t b_mfu_ghost_hits;
|
||||||
|
uint32_t b_l2_hits;
|
||||||
|
|
||||||
/* self protecting */
|
/* self protecting */
|
||||||
refcount_t b_refcnt;
|
refcount_t b_refcnt;
|
||||||
|
@ -709,7 +715,8 @@ struct l2arc_buf_hdr {
|
||||||
/* compression applied to buffer data */
|
/* compression applied to buffer data */
|
||||||
enum zio_compress b_compress;
|
enum zio_compress b_compress;
|
||||||
/* real alloc'd buffer size depending on b_compress applied */
|
/* real alloc'd buffer size depending on b_compress applied */
|
||||||
int b_asize;
|
uint32_t b_asize;
|
||||||
|
uint32_t b_hits;
|
||||||
/* temporary buffer holder for in-flight compressed data */
|
/* temporary buffer holder for in-flight compressed data */
|
||||||
void *b_tmp_cdata;
|
void *b_tmp_cdata;
|
||||||
};
|
};
|
||||||
|
@ -1137,6 +1144,54 @@ remove_reference(arc_buf_hdr_t *ab, kmutex_t *hash_lock, void *tag)
|
||||||
return (cnt);
|
return (cnt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns detailed information about a specific arc buffer. When the
|
||||||
|
* state_index argument is set the function will calculate the arc header
|
||||||
|
* list position for its arc state. Since this requires a linear traversal
|
||||||
|
* callers are strongly encourage not to do this. However, it can be helpful
|
||||||
|
* for targeted analysis so the functionality is provided.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
arc_buf_info(arc_buf_t *ab, arc_buf_info_t *abi, int state_index)
|
||||||
|
{
|
||||||
|
arc_buf_hdr_t *hdr = ab->b_hdr;
|
||||||
|
arc_state_t *state = hdr->b_state;
|
||||||
|
|
||||||
|
memset(abi, 0, sizeof(arc_buf_info_t));
|
||||||
|
abi->abi_flags = hdr->b_flags;
|
||||||
|
abi->abi_datacnt = hdr->b_datacnt;
|
||||||
|
abi->abi_state_type = state ? state->arcs_state : ARC_STATE_ANON;
|
||||||
|
abi->abi_state_contents = hdr->b_type;
|
||||||
|
abi->abi_state_index = -1;
|
||||||
|
abi->abi_size = hdr->b_size;
|
||||||
|
abi->abi_access = hdr->b_arc_access;
|
||||||
|
abi->abi_mru_hits = hdr->b_mru_hits;
|
||||||
|
abi->abi_mru_ghost_hits = hdr->b_mru_ghost_hits;
|
||||||
|
abi->abi_mfu_hits = hdr->b_mfu_hits;
|
||||||
|
abi->abi_mfu_ghost_hits = hdr->b_mfu_ghost_hits;
|
||||||
|
abi->abi_holds = refcount_count(&hdr->b_refcnt);
|
||||||
|
|
||||||
|
if (hdr->b_l2hdr) {
|
||||||
|
abi->abi_l2arc_dattr = hdr->b_l2hdr->b_daddr;
|
||||||
|
abi->abi_l2arc_asize = hdr->b_l2hdr->b_asize;
|
||||||
|
abi->abi_l2arc_compress = hdr->b_l2hdr->b_compress;
|
||||||
|
abi->abi_l2arc_hits = hdr->b_l2hdr->b_hits;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state && state_index && list_link_active(&hdr->b_arc_node)) {
|
||||||
|
list_t *list = &state->arcs_list[hdr->b_type];
|
||||||
|
arc_buf_hdr_t *h;
|
||||||
|
|
||||||
|
mutex_enter(&state->arcs_mtx);
|
||||||
|
for (h = list_head(list); h != NULL; h = list_next(list, h)) {
|
||||||
|
abi->abi_state_index++;
|
||||||
|
if (h == hdr)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mutex_exit(&state->arcs_mtx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Move the supplied buffer to the indicated state. The mutex
|
* Move the supplied buffer to the indicated state. The mutex
|
||||||
* for the buffer must be held by the caller.
|
* for the buffer must be held by the caller.
|
||||||
|
@ -1298,6 +1353,11 @@ arc_buf_alloc(spa_t *spa, int size, void *tag, arc_buf_contents_t type)
|
||||||
hdr->b_spa = spa_load_guid(spa);
|
hdr->b_spa = spa_load_guid(spa);
|
||||||
hdr->b_state = arc_anon;
|
hdr->b_state = arc_anon;
|
||||||
hdr->b_arc_access = 0;
|
hdr->b_arc_access = 0;
|
||||||
|
hdr->b_mru_hits = 0;
|
||||||
|
hdr->b_mru_ghost_hits = 0;
|
||||||
|
hdr->b_mfu_hits = 0;
|
||||||
|
hdr->b_mfu_ghost_hits = 0;
|
||||||
|
hdr->b_l2_hits = 0;
|
||||||
buf = kmem_cache_alloc(buf_cache, KM_PUSHPAGE);
|
buf = kmem_cache_alloc(buf_cache, KM_PUSHPAGE);
|
||||||
buf->b_hdr = hdr;
|
buf->b_hdr = hdr;
|
||||||
buf->b_data = NULL;
|
buf->b_data = NULL;
|
||||||
|
@ -2670,6 +2730,7 @@ arc_access(arc_buf_hdr_t *buf, kmutex_t *hash_lock)
|
||||||
ASSERT(list_link_active(&buf->b_arc_node));
|
ASSERT(list_link_active(&buf->b_arc_node));
|
||||||
} else {
|
} else {
|
||||||
buf->b_flags &= ~ARC_PREFETCH;
|
buf->b_flags &= ~ARC_PREFETCH;
|
||||||
|
atomic_inc_32(&buf->b_mru_hits);
|
||||||
ARCSTAT_BUMP(arcstat_mru_hits);
|
ARCSTAT_BUMP(arcstat_mru_hits);
|
||||||
}
|
}
|
||||||
buf->b_arc_access = now;
|
buf->b_arc_access = now;
|
||||||
|
@ -2691,6 +2752,7 @@ arc_access(arc_buf_hdr_t *buf, kmutex_t *hash_lock)
|
||||||
DTRACE_PROBE1(new_state__mfu, arc_buf_hdr_t *, buf);
|
DTRACE_PROBE1(new_state__mfu, arc_buf_hdr_t *, buf);
|
||||||
arc_change_state(arc_mfu, buf, hash_lock);
|
arc_change_state(arc_mfu, buf, hash_lock);
|
||||||
}
|
}
|
||||||
|
atomic_inc_32(&buf->b_mru_hits);
|
||||||
ARCSTAT_BUMP(arcstat_mru_hits);
|
ARCSTAT_BUMP(arcstat_mru_hits);
|
||||||
} else if (buf->b_state == arc_mru_ghost) {
|
} else if (buf->b_state == arc_mru_ghost) {
|
||||||
arc_state_t *new_state;
|
arc_state_t *new_state;
|
||||||
|
@ -2713,6 +2775,7 @@ arc_access(arc_buf_hdr_t *buf, kmutex_t *hash_lock)
|
||||||
buf->b_arc_access = ddi_get_lbolt();
|
buf->b_arc_access = ddi_get_lbolt();
|
||||||
arc_change_state(new_state, buf, hash_lock);
|
arc_change_state(new_state, buf, hash_lock);
|
||||||
|
|
||||||
|
atomic_inc_32(&buf->b_mru_ghost_hits);
|
||||||
ARCSTAT_BUMP(arcstat_mru_ghost_hits);
|
ARCSTAT_BUMP(arcstat_mru_ghost_hits);
|
||||||
} else if (buf->b_state == arc_mfu) {
|
} else if (buf->b_state == arc_mfu) {
|
||||||
/*
|
/*
|
||||||
|
@ -2728,6 +2791,7 @@ arc_access(arc_buf_hdr_t *buf, kmutex_t *hash_lock)
|
||||||
ASSERT(refcount_count(&buf->b_refcnt) == 0);
|
ASSERT(refcount_count(&buf->b_refcnt) == 0);
|
||||||
ASSERT(list_link_active(&buf->b_arc_node));
|
ASSERT(list_link_active(&buf->b_arc_node));
|
||||||
}
|
}
|
||||||
|
atomic_inc_32(&buf->b_mfu_hits);
|
||||||
ARCSTAT_BUMP(arcstat_mfu_hits);
|
ARCSTAT_BUMP(arcstat_mfu_hits);
|
||||||
buf->b_arc_access = ddi_get_lbolt();
|
buf->b_arc_access = ddi_get_lbolt();
|
||||||
} else if (buf->b_state == arc_mfu_ghost) {
|
} else if (buf->b_state == arc_mfu_ghost) {
|
||||||
|
@ -2751,6 +2815,7 @@ arc_access(arc_buf_hdr_t *buf, kmutex_t *hash_lock)
|
||||||
DTRACE_PROBE1(new_state__mfu, arc_buf_hdr_t *, buf);
|
DTRACE_PROBE1(new_state__mfu, arc_buf_hdr_t *, buf);
|
||||||
arc_change_state(new_state, buf, hash_lock);
|
arc_change_state(new_state, buf, hash_lock);
|
||||||
|
|
||||||
|
atomic_inc_32(&buf->b_mfu_ghost_hits);
|
||||||
ARCSTAT_BUMP(arcstat_mfu_ghost_hits);
|
ARCSTAT_BUMP(arcstat_mfu_ghost_hits);
|
||||||
} else if (buf->b_state == arc_l2c_only) {
|
} else if (buf->b_state == arc_l2c_only) {
|
||||||
/*
|
/*
|
||||||
|
@ -2943,6 +3008,7 @@ arc_read(zio_t *pio, spa_t *spa, const blkptr_t *bp, arc_done_func_t *done,
|
||||||
kmutex_t *hash_lock;
|
kmutex_t *hash_lock;
|
||||||
zio_t *rzio;
|
zio_t *rzio;
|
||||||
uint64_t guid = spa_load_guid(spa);
|
uint64_t guid = spa_load_guid(spa);
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
top:
|
top:
|
||||||
hdr = buf_hash_find(guid, BP_IDENTITY(bp), BP_PHYSICAL_BIRTH(bp),
|
hdr = buf_hash_find(guid, BP_IDENTITY(bp), BP_PHYSICAL_BIRTH(bp),
|
||||||
|
@ -2976,10 +3042,10 @@ top:
|
||||||
hdr->b_acb = acb;
|
hdr->b_acb = acb;
|
||||||
add_reference(hdr, hash_lock, private);
|
add_reference(hdr, hash_lock, private);
|
||||||
mutex_exit(hash_lock);
|
mutex_exit(hash_lock);
|
||||||
return (0);
|
goto out;
|
||||||
}
|
}
|
||||||
mutex_exit(hash_lock);
|
mutex_exit(hash_lock);
|
||||||
return (0);
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(hdr->b_state == arc_mru || hdr->b_state == arc_mfu);
|
ASSERT(hdr->b_state == arc_mru || hdr->b_state == arc_mfu);
|
||||||
|
@ -3133,6 +3199,7 @@ top:
|
||||||
|
|
||||||
DTRACE_PROBE1(l2arc__hit, arc_buf_hdr_t *, hdr);
|
DTRACE_PROBE1(l2arc__hit, arc_buf_hdr_t *, hdr);
|
||||||
ARCSTAT_BUMP(arcstat_l2_hits);
|
ARCSTAT_BUMP(arcstat_l2_hits);
|
||||||
|
atomic_inc_32(&hdr->b_l2hdr->b_hits);
|
||||||
|
|
||||||
cb = kmem_zalloc(sizeof (l2arc_read_callback_t),
|
cb = kmem_zalloc(sizeof (l2arc_read_callback_t),
|
||||||
KM_PUSHPAGE);
|
KM_PUSHPAGE);
|
||||||
|
@ -3174,12 +3241,12 @@ top:
|
||||||
|
|
||||||
if (*arc_flags & ARC_NOWAIT) {
|
if (*arc_flags & ARC_NOWAIT) {
|
||||||
zio_nowait(rzio);
|
zio_nowait(rzio);
|
||||||
return (0);
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(*arc_flags & ARC_WAIT);
|
ASSERT(*arc_flags & ARC_WAIT);
|
||||||
if (zio_wait(rzio) == 0)
|
if (zio_wait(rzio) == 0)
|
||||||
return (0);
|
goto out;
|
||||||
|
|
||||||
/* l2arc read error; goto zio_read() */
|
/* l2arc read error; goto zio_read() */
|
||||||
} else {
|
} else {
|
||||||
|
@ -3203,13 +3270,18 @@ top:
|
||||||
rzio = zio_read(pio, spa, bp, buf->b_data, size,
|
rzio = zio_read(pio, spa, bp, buf->b_data, size,
|
||||||
arc_read_done, buf, priority, zio_flags, zb);
|
arc_read_done, buf, priority, zio_flags, zb);
|
||||||
|
|
||||||
if (*arc_flags & ARC_WAIT)
|
if (*arc_flags & ARC_WAIT) {
|
||||||
return (zio_wait(rzio));
|
rc = zio_wait(rzio);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
ASSERT(*arc_flags & ARC_NOWAIT);
|
ASSERT(*arc_flags & ARC_NOWAIT);
|
||||||
zio_nowait(rzio);
|
zio_nowait(rzio);
|
||||||
}
|
}
|
||||||
return (0);
|
|
||||||
|
out:
|
||||||
|
spa_read_history_add(spa, zb, *arc_flags);
|
||||||
|
return (rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
arc_prune_t *
|
arc_prune_t *
|
||||||
|
@ -3463,6 +3535,11 @@ arc_release(arc_buf_t *buf, void *tag)
|
||||||
nhdr->b_buf = buf;
|
nhdr->b_buf = buf;
|
||||||
nhdr->b_state = arc_anon;
|
nhdr->b_state = arc_anon;
|
||||||
nhdr->b_arc_access = 0;
|
nhdr->b_arc_access = 0;
|
||||||
|
nhdr->b_mru_hits = 0;
|
||||||
|
nhdr->b_mru_ghost_hits = 0;
|
||||||
|
nhdr->b_mfu_hits = 0;
|
||||||
|
nhdr->b_mfu_ghost_hits = 0;
|
||||||
|
nhdr->b_l2_hits = 0;
|
||||||
nhdr->b_flags = flags & ARC_L2_WRITING;
|
nhdr->b_flags = flags & ARC_L2_WRITING;
|
||||||
nhdr->b_l2hdr = NULL;
|
nhdr->b_l2hdr = NULL;
|
||||||
nhdr->b_datacnt = 1;
|
nhdr->b_datacnt = 1;
|
||||||
|
@ -3479,6 +3556,11 @@ arc_release(arc_buf_t *buf, void *tag)
|
||||||
if (hdr->b_state != arc_anon)
|
if (hdr->b_state != arc_anon)
|
||||||
arc_change_state(arc_anon, hdr, hash_lock);
|
arc_change_state(arc_anon, hdr, hash_lock);
|
||||||
hdr->b_arc_access = 0;
|
hdr->b_arc_access = 0;
|
||||||
|
hdr->b_mru_hits = 0;
|
||||||
|
hdr->b_mru_ghost_hits = 0;
|
||||||
|
hdr->b_mfu_hits = 0;
|
||||||
|
hdr->b_mfu_ghost_hits = 0;
|
||||||
|
hdr->b_l2_hits = 0;
|
||||||
if (hash_lock)
|
if (hash_lock)
|
||||||
mutex_exit(hash_lock);
|
mutex_exit(hash_lock);
|
||||||
|
|
||||||
|
@ -3896,6 +3978,13 @@ arc_init(void)
|
||||||
list_create(&arc_l2c_only->arcs_list[ARC_BUFC_DATA],
|
list_create(&arc_l2c_only->arcs_list[ARC_BUFC_DATA],
|
||||||
sizeof (arc_buf_hdr_t), offsetof(arc_buf_hdr_t, b_arc_node));
|
sizeof (arc_buf_hdr_t), offsetof(arc_buf_hdr_t, b_arc_node));
|
||||||
|
|
||||||
|
arc_anon->arcs_state = ARC_STATE_ANON;
|
||||||
|
arc_mru->arcs_state = ARC_STATE_MRU;
|
||||||
|
arc_mru_ghost->arcs_state = ARC_STATE_MRU_GHOST;
|
||||||
|
arc_mfu->arcs_state = ARC_STATE_MFU;
|
||||||
|
arc_mfu_ghost->arcs_state = ARC_STATE_MFU_GHOST;
|
||||||
|
arc_l2c_only->arcs_state = ARC_STATE_L2C_ONLY;
|
||||||
|
|
||||||
buf_init();
|
buf_init();
|
||||||
|
|
||||||
arc_thread_exit = 0;
|
arc_thread_exit = 0;
|
||||||
|
@ -4779,6 +4868,7 @@ l2arc_write_buffers(spa_t *spa, l2arc_dev_t *dev, uint64_t target_sz,
|
||||||
l2hdr->b_compress = ZIO_COMPRESS_OFF;
|
l2hdr->b_compress = ZIO_COMPRESS_OFF;
|
||||||
l2hdr->b_asize = ab->b_size;
|
l2hdr->b_asize = ab->b_size;
|
||||||
l2hdr->b_tmp_cdata = ab->b_buf->b_data;
|
l2hdr->b_tmp_cdata = ab->b_buf->b_data;
|
||||||
|
l2hdr->b_hits = 0;
|
||||||
|
|
||||||
buf_sz = ab->b_size;
|
buf_sz = ab->b_size;
|
||||||
ab->b_l2hdr = l2hdr;
|
ab->b_l2hdr = l2hdr;
|
||||||
|
@ -5311,6 +5401,7 @@ l2arc_stop(void)
|
||||||
#if defined(_KERNEL) && defined(HAVE_SPL)
|
#if defined(_KERNEL) && defined(HAVE_SPL)
|
||||||
EXPORT_SYMBOL(arc_read);
|
EXPORT_SYMBOL(arc_read);
|
||||||
EXPORT_SYMBOL(arc_buf_remove_ref);
|
EXPORT_SYMBOL(arc_buf_remove_ref);
|
||||||
|
EXPORT_SYMBOL(arc_buf_info);
|
||||||
EXPORT_SYMBOL(arc_getbuf_func);
|
EXPORT_SYMBOL(arc_getbuf_func);
|
||||||
EXPORT_SYMBOL(arc_add_prune_callback);
|
EXPORT_SYMBOL(arc_add_prune_callback);
|
||||||
EXPORT_SYMBOL(arc_remove_prune_callback);
|
EXPORT_SYMBOL(arc_remove_prune_callback);
|
||||||
|
|
|
@ -317,6 +317,8 @@ retry:
|
||||||
|
|
||||||
for (i = 0; i < DBUF_MUTEXES; i++)
|
for (i = 0; i < DBUF_MUTEXES; i++)
|
||||||
mutex_init(&h->hash_mutexes[i], NULL, MUTEX_DEFAULT, NULL);
|
mutex_init(&h->hash_mutexes[i], NULL, MUTEX_DEFAULT, NULL);
|
||||||
|
|
||||||
|
dbuf_stats_init(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -325,6 +327,8 @@ dbuf_fini(void)
|
||||||
dbuf_hash_table_t *h = &dbuf_hash_table;
|
dbuf_hash_table_t *h = &dbuf_hash_table;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
dbuf_stats_destroy();
|
||||||
|
|
||||||
for (i = 0; i < DBUF_MUTEXES; i++)
|
for (i = 0; i < DBUF_MUTEXES; i++)
|
||||||
mutex_destroy(&h->hash_mutexes[i]);
|
mutex_destroy(&h->hash_mutexes[i]);
|
||||||
#if defined(_KERNEL) && defined(HAVE_SPL)
|
#if defined(_KERNEL) && defined(HAVE_SPL)
|
||||||
|
|
|
@ -0,0 +1,230 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/zfs_context.h>
|
||||||
|
#include <sys/dbuf.h>
|
||||||
|
#include <sys/dmu_objset.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate the index of the arc header for the state, disabled by default.
|
||||||
|
*/
|
||||||
|
int zfs_dbuf_state_index = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ==========================================================================
|
||||||
|
* Dbuf Hash Read Routines
|
||||||
|
* ==========================================================================
|
||||||
|
*/
|
||||||
|
typedef struct dbuf_stats_t {
|
||||||
|
kmutex_t lock;
|
||||||
|
kstat_t *kstat;
|
||||||
|
dbuf_hash_table_t *hash;
|
||||||
|
int idx;
|
||||||
|
} dbuf_stats_t;
|
||||||
|
|
||||||
|
static dbuf_stats_t dbuf_stats_hash_table;
|
||||||
|
|
||||||
|
static int
|
||||||
|
dbuf_stats_hash_table_headers(char *buf, size_t size)
|
||||||
|
{
|
||||||
|
size = snprintf(buf, size - 1,
|
||||||
|
"%-88s | %-124s | %s\n"
|
||||||
|
"%-16s %-8s %-8s %-8s %-8s %-8s %-8s %-5s %-5s %5s | "
|
||||||
|
"%-5s %-5s %-6s %-8s %-6s %-8s %-12s "
|
||||||
|
"%-6s %-6s %-6s %-6s %-6s %-8s %-8s %-8s %-5s | "
|
||||||
|
"%-6s %-6s %-8s %-8s %-6s %-6s %-5s %-8s %-8s\n",
|
||||||
|
"dbuf", "arcbuf", "dnode", "pool", "objset", "object", "level",
|
||||||
|
"blkid", "offset", "dbsize", "meta", "state", "dbholds", "list",
|
||||||
|
"atype", "index", "flags", "count", "asize", "access", "mru", "gmru",
|
||||||
|
"mfu", "gmfu", "l2", "l2_dattr", "l2_asize", "l2_comp", "aholds",
|
||||||
|
"dtype", "btype", "data_bs", "meta_bs", "bsize",
|
||||||
|
"lvls", "dholds", "blocks", "dsize");
|
||||||
|
buf[size] = '\0';
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
__dbuf_stats_hash_table_data(char *buf, size_t size, dmu_buf_impl_t *db)
|
||||||
|
{
|
||||||
|
arc_buf_info_t abi = { 0 };
|
||||||
|
dmu_object_info_t doi = { 0 };
|
||||||
|
dnode_t *dn = DB_DNODE(db);
|
||||||
|
|
||||||
|
if (db->db_buf)
|
||||||
|
arc_buf_info(db->db_buf, &abi, zfs_dbuf_state_index);
|
||||||
|
|
||||||
|
if (dn)
|
||||||
|
__dmu_object_info_from_dnode(dn, &doi);
|
||||||
|
|
||||||
|
size = snprintf(buf, size - 1,
|
||||||
|
"%-16s %-8llu %-8lld %-8lld %-8lld %-8llu %-8llu %-5d %-5d %-5lu | "
|
||||||
|
"%-5d %-5d %-6lld 0x%-6x %-6lu %-8llu %-12llu "
|
||||||
|
"%-6lu %-6lu %-6lu %-6lu %-6lu %-8llu %-8llu %-8d %-5lu | "
|
||||||
|
"%-6d %-6d %-8lu %-8lu %-6llu %-6lu %-5lu %-8llu %-8llu\n",
|
||||||
|
/* dmu_buf_impl_t */
|
||||||
|
spa_name(dn->dn_objset->os_spa),
|
||||||
|
(u_longlong_t)dmu_objset_id(db->db_objset),
|
||||||
|
(longlong_t)db->db.db_object,
|
||||||
|
(longlong_t)db->db_level,
|
||||||
|
(longlong_t)db->db_blkid,
|
||||||
|
(u_longlong_t)db->db.db_offset,
|
||||||
|
(u_longlong_t)db->db.db_size,
|
||||||
|
!!dbuf_is_metadata(db),
|
||||||
|
db->db_state,
|
||||||
|
(ulong_t)refcount_count(&db->db_holds),
|
||||||
|
/* arc_buf_info_t */
|
||||||
|
abi.abi_state_type,
|
||||||
|
abi.abi_state_contents,
|
||||||
|
(longlong_t)abi.abi_state_index,
|
||||||
|
abi.abi_flags,
|
||||||
|
(ulong_t)abi.abi_datacnt,
|
||||||
|
(u_longlong_t)abi.abi_size,
|
||||||
|
(u_longlong_t)abi.abi_access,
|
||||||
|
(ulong_t)abi.abi_mru_hits,
|
||||||
|
(ulong_t)abi.abi_mru_ghost_hits,
|
||||||
|
(ulong_t)abi.abi_mfu_hits,
|
||||||
|
(ulong_t)abi.abi_mfu_ghost_hits,
|
||||||
|
(ulong_t)abi.abi_l2arc_hits,
|
||||||
|
(u_longlong_t)abi.abi_l2arc_dattr,
|
||||||
|
(u_longlong_t)abi.abi_l2arc_asize,
|
||||||
|
abi.abi_l2arc_compress,
|
||||||
|
(ulong_t)abi.abi_holds,
|
||||||
|
/* dmu_object_info_t */
|
||||||
|
doi.doi_type,
|
||||||
|
doi.doi_bonus_type,
|
||||||
|
(ulong_t)doi.doi_data_block_size,
|
||||||
|
(ulong_t)doi.doi_metadata_block_size,
|
||||||
|
(u_longlong_t)doi.doi_bonus_size,
|
||||||
|
(ulong_t)doi.doi_indirection,
|
||||||
|
(ulong_t)refcount_count(&dn->dn_holds),
|
||||||
|
(u_longlong_t)doi.doi_fill_count,
|
||||||
|
(u_longlong_t)doi.doi_max_offset);
|
||||||
|
buf[size] = '\0';
|
||||||
|
|
||||||
|
return (size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
dbuf_stats_hash_table_data(char *buf, size_t size, void *data)
|
||||||
|
{
|
||||||
|
dbuf_stats_t *dsh = (dbuf_stats_t *)data;
|
||||||
|
dbuf_hash_table_t *h = dsh->hash;
|
||||||
|
dmu_buf_impl_t *db;
|
||||||
|
int length, error = 0;
|
||||||
|
|
||||||
|
ASSERT3S(dsh->idx, >=, 0);
|
||||||
|
ASSERT3S(dsh->idx, <=, h->hash_table_mask);
|
||||||
|
memset(buf, 0, size);
|
||||||
|
|
||||||
|
mutex_enter(DBUF_HASH_MUTEX(h, dsh->idx));
|
||||||
|
for (db = h->hash_table[dsh->idx]; db != NULL; db = db->db_hash_next) {
|
||||||
|
/*
|
||||||
|
* Returning ENOMEM will cause the data and header functions
|
||||||
|
* to be called with a larger scratch buffers.
|
||||||
|
*/
|
||||||
|
if (size < 512) {
|
||||||
|
error = ENOMEM;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_enter(&db->db_mtx);
|
||||||
|
mutex_exit(DBUF_HASH_MUTEX(h, dsh->idx));
|
||||||
|
|
||||||
|
length = __dbuf_stats_hash_table_data(buf, size, db);
|
||||||
|
buf += length;
|
||||||
|
size -= length;
|
||||||
|
|
||||||
|
mutex_exit(&db->db_mtx);
|
||||||
|
mutex_enter(DBUF_HASH_MUTEX(h, dsh->idx));
|
||||||
|
}
|
||||||
|
mutex_exit(DBUF_HASH_MUTEX(h, dsh->idx));
|
||||||
|
|
||||||
|
return (error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *
|
||||||
|
dbuf_stats_hash_table_addr(kstat_t *ksp, loff_t n)
|
||||||
|
{
|
||||||
|
dbuf_stats_t *dsh = ksp->ks_private;
|
||||||
|
|
||||||
|
ASSERT(MUTEX_HELD(&dsh->lock));
|
||||||
|
|
||||||
|
if (n <= dsh->hash->hash_table_mask) {
|
||||||
|
dsh->idx = n;
|
||||||
|
return (dsh);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
dbuf_stats_hash_table_init(dbuf_hash_table_t *hash)
|
||||||
|
{
|
||||||
|
dbuf_stats_t *dsh = &dbuf_stats_hash_table;
|
||||||
|
kstat_t *ksp;
|
||||||
|
|
||||||
|
mutex_init(&dsh->lock, NULL, MUTEX_DEFAULT, NULL);
|
||||||
|
dsh->hash = hash;
|
||||||
|
|
||||||
|
ksp = kstat_create("zfs", 0, "dbufs", "misc",
|
||||||
|
KSTAT_TYPE_RAW, 0, KSTAT_FLAG_VIRTUAL);
|
||||||
|
dsh->kstat = ksp;
|
||||||
|
|
||||||
|
if (ksp) {
|
||||||
|
ksp->ks_lock = &dsh->lock;
|
||||||
|
ksp->ks_ndata = UINT32_MAX;
|
||||||
|
ksp->ks_private = dsh;
|
||||||
|
kstat_set_raw_ops(ksp, dbuf_stats_hash_table_headers,
|
||||||
|
dbuf_stats_hash_table_data, dbuf_stats_hash_table_addr);
|
||||||
|
kstat_install(ksp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
dbuf_stats_hash_table_destroy(void)
|
||||||
|
{
|
||||||
|
dbuf_stats_t *dsh = &dbuf_stats_hash_table;
|
||||||
|
kstat_t *ksp;
|
||||||
|
|
||||||
|
ksp = dsh->kstat;
|
||||||
|
if (ksp)
|
||||||
|
kstat_delete(ksp);
|
||||||
|
|
||||||
|
mutex_destroy(&dsh->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
dbuf_stats_init(dbuf_hash_table_t *hash)
|
||||||
|
{
|
||||||
|
dbuf_stats_hash_table_init(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
dbuf_stats_destroy(void)
|
||||||
|
{
|
||||||
|
dbuf_stats_hash_table_destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(_KERNEL) && defined(HAVE_SPL)
|
||||||
|
module_param(zfs_dbuf_state_index, int, 0644);
|
||||||
|
MODULE_PARM_DESC(zfs_dbuf_state_index, "Calculate arc header index");
|
||||||
|
#endif
|
|
@ -1815,16 +1815,11 @@ dmu_offset_next(objset_t *os, uint64_t object, boolean_t hole, uint64_t *off)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
dmu_object_info_from_dnode(dnode_t *dn, dmu_object_info_t *doi)
|
__dmu_object_info_from_dnode(dnode_t *dn, dmu_object_info_t *doi)
|
||||||
{
|
{
|
||||||
dnode_phys_t *dnp;
|
dnode_phys_t *dnp = dn->dn_phys;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
rw_enter(&dn->dn_struct_rwlock, RW_READER);
|
|
||||||
mutex_enter(&dn->dn_mtx);
|
|
||||||
|
|
||||||
dnp = dn->dn_phys;
|
|
||||||
|
|
||||||
doi->doi_data_block_size = dn->dn_datablksz;
|
doi->doi_data_block_size = dn->dn_datablksz;
|
||||||
doi->doi_metadata_block_size = dn->dn_indblkshift ?
|
doi->doi_metadata_block_size = dn->dn_indblkshift ?
|
||||||
1ULL << dn->dn_indblkshift : 0;
|
1ULL << dn->dn_indblkshift : 0;
|
||||||
|
@ -1839,6 +1834,15 @@ dmu_object_info_from_dnode(dnode_t *dn, dmu_object_info_t *doi)
|
||||||
doi->doi_fill_count = 0;
|
doi->doi_fill_count = 0;
|
||||||
for (i = 0; i < dnp->dn_nblkptr; i++)
|
for (i = 0; i < dnp->dn_nblkptr; i++)
|
||||||
doi->doi_fill_count += dnp->dn_blkptr[i].blk_fill;
|
doi->doi_fill_count += dnp->dn_blkptr[i].blk_fill;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
dmu_object_info_from_dnode(dnode_t *dn, dmu_object_info_t *doi)
|
||||||
|
{
|
||||||
|
rw_enter(&dn->dn_struct_rwlock, RW_READER);
|
||||||
|
mutex_enter(&dn->dn_mtx);
|
||||||
|
|
||||||
|
__dmu_object_info_from_dnode(dn, doi);
|
||||||
|
|
||||||
mutex_exit(&dn->dn_mtx);
|
mutex_exit(&dn->dn_mtx);
|
||||||
rw_exit(&dn->dn_struct_rwlock);
|
rw_exit(&dn->dn_struct_rwlock);
|
||||||
|
|
|
@ -517,6 +517,9 @@ traverse_impl(spa_t *spa, dsl_dataset_t *ds, uint64_t objset, blkptr_t *rootbp,
|
||||||
mutex_init(&pd->pd_mtx, NULL, MUTEX_DEFAULT, NULL);
|
mutex_init(&pd->pd_mtx, NULL, MUTEX_DEFAULT, NULL);
|
||||||
cv_init(&pd->pd_cv, NULL, CV_DEFAULT, NULL);
|
cv_init(&pd->pd_cv, NULL, CV_DEFAULT, NULL);
|
||||||
|
|
||||||
|
SET_BOOKMARK(czb, td->td_objset,
|
||||||
|
ZB_ROOT_OBJECT, ZB_ROOT_LEVEL, ZB_ROOT_BLKID);
|
||||||
|
|
||||||
/* See comment on ZIL traversal in dsl_scan_visitds. */
|
/* See comment on ZIL traversal in dsl_scan_visitds. */
|
||||||
if (ds != NULL && !dsl_dataset_is_snapshot(ds) && !BP_IS_HOLE(rootbp)) {
|
if (ds != NULL && !dsl_dataset_is_snapshot(ds) && !BP_IS_HOLE(rootbp)) {
|
||||||
uint32_t flags = ARC_WAIT;
|
uint32_t flags = ARC_WAIT;
|
||||||
|
@ -525,7 +528,7 @@ traverse_impl(spa_t *spa, dsl_dataset_t *ds, uint64_t objset, blkptr_t *rootbp,
|
||||||
|
|
||||||
err = arc_read(NULL, td->td_spa, rootbp,
|
err = arc_read(NULL, td->td_spa, rootbp,
|
||||||
arc_getbuf_func, &buf,
|
arc_getbuf_func, &buf,
|
||||||
ZIO_PRIORITY_ASYNC_READ, ZIO_FLAG_CANFAIL, &flags, NULL);
|
ZIO_PRIORITY_ASYNC_READ, ZIO_FLAG_CANFAIL, &flags, czb);
|
||||||
if (err != 0)
|
if (err != 0)
|
||||||
return (err);
|
return (err);
|
||||||
|
|
||||||
|
@ -539,8 +542,6 @@ traverse_impl(spa_t *spa, dsl_dataset_t *ds, uint64_t objset, blkptr_t *rootbp,
|
||||||
td, TQ_NOQUEUE))
|
td, TQ_NOQUEUE))
|
||||||
pd->pd_exited = B_TRUE;
|
pd->pd_exited = B_TRUE;
|
||||||
|
|
||||||
SET_BOOKMARK(czb, td->td_objset,
|
|
||||||
ZB_ROOT_OBJECT, ZB_ROOT_LEVEL, ZB_ROOT_BLKID);
|
|
||||||
err = traverse_visitbp(td, NULL, rootbp, czb);
|
err = traverse_visitbp(td, NULL, rootbp, czb);
|
||||||
|
|
||||||
mutex_enter(&pd->pd_mtx);
|
mutex_enter(&pd->pd_mtx);
|
||||||
|
|
|
@ -1077,7 +1077,7 @@ dmu_tx_unassign(dmu_tx_t *tx)
|
||||||
int
|
int
|
||||||
dmu_tx_assign(dmu_tx_t *tx, txg_how_t txg_how)
|
dmu_tx_assign(dmu_tx_t *tx, txg_how_t txg_how)
|
||||||
{
|
{
|
||||||
hrtime_t before, after;
|
hrtime_t before;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
ASSERT(tx->tx_txg == 0);
|
ASSERT(tx->tx_txg == 0);
|
||||||
|
@ -1100,10 +1100,7 @@ dmu_tx_assign(dmu_tx_t *tx, txg_how_t txg_how)
|
||||||
|
|
||||||
txg_rele_to_quiesce(&tx->tx_txgh);
|
txg_rele_to_quiesce(&tx->tx_txgh);
|
||||||
|
|
||||||
after = gethrtime();
|
spa_tx_assign_add_nsecs(tx->tx_pool->dp_spa, gethrtime() - before);
|
||||||
|
|
||||||
dsl_pool_tx_assign_add_usecs(tx->tx_pool,
|
|
||||||
(after - before) / NSEC_PER_USEC);
|
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,6 @@
|
||||||
int zfs_no_write_throttle = 0;
|
int zfs_no_write_throttle = 0;
|
||||||
int zfs_write_limit_shift = 3; /* 1/8th of physical memory */
|
int zfs_write_limit_shift = 3; /* 1/8th of physical memory */
|
||||||
int zfs_txg_synctime_ms = 1000; /* target millisecs to sync a txg */
|
int zfs_txg_synctime_ms = 1000; /* target millisecs to sync a txg */
|
||||||
int zfs_txg_history = 60; /* statistics for the last N txgs */
|
|
||||||
|
|
||||||
unsigned long zfs_write_limit_min = 32 << 20; /* min write limit is 32MB */
|
unsigned long zfs_write_limit_min = 32 << 20; /* min write limit is 32MB */
|
||||||
unsigned long zfs_write_limit_max = 0; /* max data payload per txg */
|
unsigned long zfs_write_limit_max = 0; /* max data payload per txg */
|
||||||
|
@ -59,200 +58,6 @@ kmutex_t zfs_write_limit_lock;
|
||||||
|
|
||||||
static pgcnt_t old_physmem = 0;
|
static pgcnt_t old_physmem = 0;
|
||||||
|
|
||||||
static void
|
|
||||||
dsl_pool_tx_assign_init(dsl_pool_t *dp, unsigned int ndata)
|
|
||||||
{
|
|
||||||
kstat_named_t *ks;
|
|
||||||
char name[KSTAT_STRLEN];
|
|
||||||
int i, data_size = ndata * sizeof(kstat_named_t);
|
|
||||||
|
|
||||||
(void) snprintf(name, KSTAT_STRLEN, "dmu_tx_assign-%s",
|
|
||||||
spa_name(dp->dp_spa));
|
|
||||||
|
|
||||||
dp->dp_tx_assign_size = ndata;
|
|
||||||
|
|
||||||
if (data_size)
|
|
||||||
dp->dp_tx_assign_buckets = kmem_alloc(data_size, KM_SLEEP);
|
|
||||||
else
|
|
||||||
dp->dp_tx_assign_buckets = NULL;
|
|
||||||
|
|
||||||
for (i = 0; i < dp->dp_tx_assign_size; i++) {
|
|
||||||
ks = &dp->dp_tx_assign_buckets[i];
|
|
||||||
ks->data_type = KSTAT_DATA_UINT64;
|
|
||||||
ks->value.ui64 = 0;
|
|
||||||
(void) snprintf(ks->name, KSTAT_STRLEN, "%u us", 1 << i);
|
|
||||||
}
|
|
||||||
|
|
||||||
dp->dp_tx_assign_kstat = kstat_create("zfs", 0, name, "misc",
|
|
||||||
KSTAT_TYPE_NAMED, 0, KSTAT_FLAG_VIRTUAL);
|
|
||||||
|
|
||||||
if (dp->dp_tx_assign_kstat) {
|
|
||||||
dp->dp_tx_assign_kstat->ks_data = dp->dp_tx_assign_buckets;
|
|
||||||
dp->dp_tx_assign_kstat->ks_ndata = dp->dp_tx_assign_size;
|
|
||||||
dp->dp_tx_assign_kstat->ks_data_size = data_size;
|
|
||||||
kstat_install(dp->dp_tx_assign_kstat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
dsl_pool_tx_assign_destroy(dsl_pool_t *dp)
|
|
||||||
{
|
|
||||||
if (dp->dp_tx_assign_buckets)
|
|
||||||
kmem_free(dp->dp_tx_assign_buckets,
|
|
||||||
dp->dp_tx_assign_size * sizeof(kstat_named_t));
|
|
||||||
|
|
||||||
if (dp->dp_tx_assign_kstat)
|
|
||||||
kstat_delete(dp->dp_tx_assign_kstat);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
dsl_pool_tx_assign_add_usecs(dsl_pool_t *dp, uint64_t usecs)
|
|
||||||
{
|
|
||||||
uint64_t idx = 0;
|
|
||||||
|
|
||||||
while (((1 << idx) < usecs) && (idx < dp->dp_tx_assign_size - 1))
|
|
||||||
idx++;
|
|
||||||
|
|
||||||
atomic_inc_64(&dp->dp_tx_assign_buckets[idx].value.ui64);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
dsl_pool_txg_history_update(kstat_t *ksp, int rw)
|
|
||||||
{
|
|
||||||
dsl_pool_t *dp = ksp->ks_private;
|
|
||||||
txg_history_t *th;
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
if (rw == KSTAT_WRITE)
|
|
||||||
return (EACCES);
|
|
||||||
|
|
||||||
if (ksp->ks_data)
|
|
||||||
kmem_free(ksp->ks_data, ksp->ks_data_size);
|
|
||||||
|
|
||||||
mutex_enter(&dp->dp_lock);
|
|
||||||
|
|
||||||
ksp->ks_ndata = dp->dp_txg_history_size;
|
|
||||||
ksp->ks_data_size = dp->dp_txg_history_size * sizeof(kstat_txg_t);
|
|
||||||
if (ksp->ks_data_size > 0)
|
|
||||||
ksp->ks_data = kmem_alloc(ksp->ks_data_size, KM_PUSHPAGE);
|
|
||||||
|
|
||||||
/* Traversed oldest to youngest for the most readable kstat output */
|
|
||||||
for (th = list_tail(&dp->dp_txg_history); th != NULL;
|
|
||||||
th = list_prev(&dp->dp_txg_history, th)) {
|
|
||||||
mutex_enter(&th->th_lock);
|
|
||||||
ASSERT3S(i + sizeof(kstat_txg_t), <=, ksp->ks_data_size);
|
|
||||||
memcpy(ksp->ks_data + i, &th->th_kstat, sizeof(kstat_txg_t));
|
|
||||||
i += sizeof(kstat_txg_t);
|
|
||||||
mutex_exit(&th->th_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
mutex_exit(&dp->dp_lock);
|
|
||||||
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
dsl_pool_txg_history_init(dsl_pool_t *dp, uint64_t txg)
|
|
||||||
{
|
|
||||||
char name[KSTAT_STRLEN];
|
|
||||||
|
|
||||||
list_create(&dp->dp_txg_history, sizeof (txg_history_t),
|
|
||||||
offsetof(txg_history_t, th_link));
|
|
||||||
dsl_pool_txg_history_add(dp, txg);
|
|
||||||
|
|
||||||
(void) snprintf(name, KSTAT_STRLEN, "txgs-%s", spa_name(dp->dp_spa));
|
|
||||||
dp->dp_txg_kstat = kstat_create("zfs", 0, name, "misc",
|
|
||||||
KSTAT_TYPE_TXG, 0, KSTAT_FLAG_VIRTUAL);
|
|
||||||
if (dp->dp_txg_kstat) {
|
|
||||||
dp->dp_txg_kstat->ks_data = NULL;
|
|
||||||
dp->dp_txg_kstat->ks_private = dp;
|
|
||||||
dp->dp_txg_kstat->ks_update = dsl_pool_txg_history_update;
|
|
||||||
kstat_install(dp->dp_txg_kstat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
dsl_pool_txg_history_destroy(dsl_pool_t *dp)
|
|
||||||
{
|
|
||||||
txg_history_t *th;
|
|
||||||
|
|
||||||
if (dp->dp_txg_kstat) {
|
|
||||||
if (dp->dp_txg_kstat->ks_data)
|
|
||||||
kmem_free(dp->dp_txg_kstat->ks_data,
|
|
||||||
dp->dp_txg_kstat->ks_data_size);
|
|
||||||
|
|
||||||
kstat_delete(dp->dp_txg_kstat);
|
|
||||||
}
|
|
||||||
|
|
||||||
mutex_enter(&dp->dp_lock);
|
|
||||||
while ((th = list_remove_head(&dp->dp_txg_history))) {
|
|
||||||
dp->dp_txg_history_size--;
|
|
||||||
mutex_destroy(&th->th_lock);
|
|
||||||
kmem_free(th, sizeof(txg_history_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT3U(dp->dp_txg_history_size, ==, 0);
|
|
||||||
list_destroy(&dp->dp_txg_history);
|
|
||||||
mutex_exit(&dp->dp_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
txg_history_t *
|
|
||||||
dsl_pool_txg_history_add(dsl_pool_t *dp, uint64_t txg)
|
|
||||||
{
|
|
||||||
txg_history_t *th, *rm;
|
|
||||||
|
|
||||||
th = kmem_zalloc(sizeof(txg_history_t), KM_PUSHPAGE);
|
|
||||||
mutex_init(&th->th_lock, NULL, MUTEX_DEFAULT, NULL);
|
|
||||||
th->th_kstat.txg = txg;
|
|
||||||
th->th_kstat.state = TXG_STATE_OPEN;
|
|
||||||
th->th_kstat.birth = gethrtime();
|
|
||||||
|
|
||||||
mutex_enter(&dp->dp_lock);
|
|
||||||
|
|
||||||
list_insert_head(&dp->dp_txg_history, th);
|
|
||||||
dp->dp_txg_history_size++;
|
|
||||||
|
|
||||||
while (dp->dp_txg_history_size > zfs_txg_history) {
|
|
||||||
dp->dp_txg_history_size--;
|
|
||||||
rm = list_remove_tail(&dp->dp_txg_history);
|
|
||||||
mutex_destroy(&rm->th_lock);
|
|
||||||
kmem_free(rm, sizeof(txg_history_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
mutex_exit(&dp->dp_lock);
|
|
||||||
|
|
||||||
return (th);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Traversed youngest to oldest because lookups are only done for open
|
|
||||||
* or syncing txgs which are guaranteed to be at the head of the list.
|
|
||||||
* The txg_history_t structure will be returned locked.
|
|
||||||
*/
|
|
||||||
txg_history_t *
|
|
||||||
dsl_pool_txg_history_get(dsl_pool_t *dp, uint64_t txg)
|
|
||||||
{
|
|
||||||
txg_history_t *th;
|
|
||||||
|
|
||||||
mutex_enter(&dp->dp_lock);
|
|
||||||
for (th = list_head(&dp->dp_txg_history); th != NULL;
|
|
||||||
th = list_next(&dp->dp_txg_history, th)) {
|
|
||||||
if (th->th_kstat.txg == txg) {
|
|
||||||
mutex_enter(&th->th_lock);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mutex_exit(&dp->dp_lock);
|
|
||||||
|
|
||||||
return (th);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
dsl_pool_txg_history_put(txg_history_t *th)
|
|
||||||
{
|
|
||||||
mutex_exit(&th->th_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
int
|
||||||
dsl_pool_open_special_dir(dsl_pool_t *dp, const char *name, dsl_dir_t **ddp)
|
dsl_pool_open_special_dir(dsl_pool_t *dp, const char *name, dsl_dir_t **ddp)
|
||||||
{
|
{
|
||||||
|
@ -295,9 +100,6 @@ dsl_pool_open_impl(spa_t *spa, uint64_t txg)
|
||||||
dp->dp_iput_taskq = taskq_create("zfs_iput_taskq", 1, minclsyspri,
|
dp->dp_iput_taskq = taskq_create("zfs_iput_taskq", 1, minclsyspri,
|
||||||
1, 4, 0);
|
1, 4, 0);
|
||||||
|
|
||||||
dsl_pool_txg_history_init(dp, txg);
|
|
||||||
dsl_pool_tx_assign_init(dp, 32);
|
|
||||||
|
|
||||||
return (dp);
|
return (dp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -438,8 +240,6 @@ dsl_pool_close(dsl_pool_t *dp)
|
||||||
arc_flush(dp->dp_spa);
|
arc_flush(dp->dp_spa);
|
||||||
txg_fini(dp);
|
txg_fini(dp);
|
||||||
dsl_scan_fini(dp);
|
dsl_scan_fini(dp);
|
||||||
dsl_pool_tx_assign_destroy(dp);
|
|
||||||
dsl_pool_txg_history_destroy(dp);
|
|
||||||
rrw_destroy(&dp->dp_config_rwlock);
|
rrw_destroy(&dp->dp_config_rwlock);
|
||||||
mutex_destroy(&dp->dp_lock);
|
mutex_destroy(&dp->dp_lock);
|
||||||
taskq_destroy(dp->dp_iput_taskq);
|
taskq_destroy(dp->dp_iput_taskq);
|
||||||
|
@ -1241,9 +1041,6 @@ MODULE_PARM_DESC(zfs_write_limit_shift, "log2(fraction of memory) per txg");
|
||||||
module_param(zfs_txg_synctime_ms, int, 0644);
|
module_param(zfs_txg_synctime_ms, int, 0644);
|
||||||
MODULE_PARM_DESC(zfs_txg_synctime_ms, "Target milliseconds between txg sync");
|
MODULE_PARM_DESC(zfs_txg_synctime_ms, "Target milliseconds between txg sync");
|
||||||
|
|
||||||
module_param(zfs_txg_history, int, 0644);
|
|
||||||
MODULE_PARM_DESC(zfs_txg_history, "Historic statistics for the last N txgs");
|
|
||||||
|
|
||||||
module_param(zfs_write_limit_min, ulong, 0444);
|
module_param(zfs_write_limit_min, ulong, 0444);
|
||||||
MODULE_PARM_DESC(zfs_write_limit_min, "Min txg write limit");
|
MODULE_PARM_DESC(zfs_write_limit_min, "Min txg write limit");
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
#include <sys/metaslab_impl.h>
|
#include <sys/metaslab_impl.h>
|
||||||
#include <sys/arc.h>
|
#include <sys/arc.h>
|
||||||
#include <sys/ddt.h>
|
#include <sys/ddt.h>
|
||||||
|
#include <sys/kstat.h>
|
||||||
#include "zfs_prop.h"
|
#include "zfs_prop.h"
|
||||||
#include "zfeature_common.h"
|
#include "zfeature_common.h"
|
||||||
|
|
||||||
|
@ -253,7 +254,6 @@ unsigned long zfs_deadman_synctime = 1000ULL;
|
||||||
*/
|
*/
|
||||||
int zfs_deadman_enabled = 1;
|
int zfs_deadman_enabled = 1;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ==========================================================================
|
* ==========================================================================
|
||||||
* SPA config locking
|
* SPA config locking
|
||||||
|
@ -495,6 +495,7 @@ spa_add(const char *name, nvlist_t *config, const char *altroot)
|
||||||
|
|
||||||
refcount_create(&spa->spa_refcount);
|
refcount_create(&spa->spa_refcount);
|
||||||
spa_config_lock_init(spa);
|
spa_config_lock_init(spa);
|
||||||
|
spa_stats_init(spa);
|
||||||
|
|
||||||
avl_add(&spa_namespace_avl, spa);
|
avl_add(&spa_namespace_avl, spa);
|
||||||
|
|
||||||
|
@ -580,6 +581,7 @@ spa_remove(spa_t *spa)
|
||||||
|
|
||||||
refcount_destroy(&spa->spa_refcount);
|
refcount_destroy(&spa->spa_refcount);
|
||||||
|
|
||||||
|
spa_stats_destroy(spa);
|
||||||
spa_config_lock_destroy(spa);
|
spa_config_lock_destroy(spa);
|
||||||
|
|
||||||
for (t = 0; t < TXG_SIZE; t++)
|
for (t = 0; t < TXG_SIZE; t++)
|
||||||
|
|
|
@ -0,0 +1,636 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/zfs_context.h>
|
||||||
|
#include <sys/spa_impl.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Keeps stats on last N reads per spa_t, disabled by default.
|
||||||
|
*/
|
||||||
|
int zfs_read_history = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Include cache hits in history, disabled by default.
|
||||||
|
*/
|
||||||
|
int zfs_read_history_hits = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Keeps stats on the last N txgs, disabled by default.
|
||||||
|
*/
|
||||||
|
int zfs_txg_history = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ==========================================================================
|
||||||
|
* SPA Read History Routines
|
||||||
|
* ==========================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read statistics - Information exported regarding each arc_read call
|
||||||
|
*/
|
||||||
|
typedef struct spa_read_history {
|
||||||
|
uint64_t uid; /* unique identifier */
|
||||||
|
hrtime_t start; /* time read completed */
|
||||||
|
uint64_t objset; /* read from this objset */
|
||||||
|
uint64_t object; /* read of this object number */
|
||||||
|
uint64_t level; /* block's indirection level */
|
||||||
|
uint64_t blkid; /* read of this block id */
|
||||||
|
char origin[24]; /* read originated from here */
|
||||||
|
uint32_t aflags; /* ARC flags (cached, prefetch, etc.) */
|
||||||
|
pid_t pid; /* PID of task doing read */
|
||||||
|
char comm[16]; /* process name of task doing read */
|
||||||
|
list_node_t srh_link;
|
||||||
|
} spa_read_history_t;
|
||||||
|
|
||||||
|
static int
|
||||||
|
spa_read_history_headers(char *buf, size_t size)
|
||||||
|
{
|
||||||
|
size = snprintf(buf, size - 1, "%-8s %-16s %-8s %-8s %-8s %-8s %-8s "
|
||||||
|
"%-24s %-8s %-16s\n", "UID", "start", "objset", "object",
|
||||||
|
"level", "blkid", "aflags", "origin", "pid", "process");
|
||||||
|
buf[size] = '\0';
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
spa_read_history_data(char *buf, size_t size, void *data)
|
||||||
|
{
|
||||||
|
spa_read_history_t *srh = (spa_read_history_t *)data;
|
||||||
|
|
||||||
|
size = snprintf(buf, size - 1, "%-8llu %-16llu 0x%-6llx "
|
||||||
|
"%-8lli %-8lli %-8lli 0x%-6x %-24s %-8i %-16s\n",
|
||||||
|
(u_longlong_t)srh->uid, srh->start,
|
||||||
|
(longlong_t)srh->objset, (longlong_t)srh->object,
|
||||||
|
(longlong_t)srh->level, (longlong_t)srh->blkid,
|
||||||
|
srh->aflags, srh->origin, srh->pid, srh->comm);
|
||||||
|
buf[size] = '\0';
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate the address for the next spa_stats_history_t entry. The
|
||||||
|
* ssh->lock will be held until ksp->ks_ndata entries are processed.
|
||||||
|
*/
|
||||||
|
static void *
|
||||||
|
spa_read_history_addr(kstat_t *ksp, loff_t n)
|
||||||
|
{
|
||||||
|
spa_t *spa = ksp->ks_private;
|
||||||
|
spa_stats_history_t *ssh = &spa->spa_stats.read_history;
|
||||||
|
|
||||||
|
ASSERT(MUTEX_HELD(&ssh->lock));
|
||||||
|
|
||||||
|
if (n == 0)
|
||||||
|
ssh->private = list_tail(&ssh->list);
|
||||||
|
else if (ssh->private)
|
||||||
|
ssh->private = list_prev(&ssh->list, ssh->private);
|
||||||
|
|
||||||
|
return (ssh->private);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When the kstat is written discard all spa_read_history_t entires. The
|
||||||
|
* ssh->lock will be held until ksp->ks_ndata entries are processed.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
spa_read_history_update(kstat_t *ksp, int rw)
|
||||||
|
{
|
||||||
|
spa_t *spa = ksp->ks_private;
|
||||||
|
spa_stats_history_t *ssh = &spa->spa_stats.read_history;
|
||||||
|
|
||||||
|
if (rw == KSTAT_WRITE) {
|
||||||
|
spa_read_history_t *srh;
|
||||||
|
|
||||||
|
while ((srh = list_remove_head(&ssh->list))) {
|
||||||
|
ssh->size--;
|
||||||
|
kmem_free(srh, sizeof(spa_read_history_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT3U(ssh->size, ==, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ksp->ks_ndata = ssh->size;
|
||||||
|
ksp->ks_data_size = ssh->size * sizeof(spa_read_history_t);
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
spa_read_history_init(spa_t *spa)
|
||||||
|
{
|
||||||
|
spa_stats_history_t *ssh = &spa->spa_stats.read_history;
|
||||||
|
char name[KSTAT_STRLEN];
|
||||||
|
kstat_t *ksp;
|
||||||
|
|
||||||
|
mutex_init(&ssh->lock, NULL, MUTEX_DEFAULT, NULL);
|
||||||
|
list_create(&ssh->list, sizeof (spa_read_history_t),
|
||||||
|
offsetof(spa_read_history_t, srh_link));
|
||||||
|
|
||||||
|
ssh->count = 0;
|
||||||
|
ssh->size = 0;
|
||||||
|
ssh->private = NULL;
|
||||||
|
|
||||||
|
(void) snprintf(name, KSTAT_STRLEN, "zfs/%s", spa_name(spa));
|
||||||
|
name[KSTAT_STRLEN-1] = '\0';
|
||||||
|
|
||||||
|
ksp = kstat_create(name, 0, "reads", "misc",
|
||||||
|
KSTAT_TYPE_RAW, 0, KSTAT_FLAG_VIRTUAL);
|
||||||
|
ssh->kstat = ksp;
|
||||||
|
|
||||||
|
if (ksp) {
|
||||||
|
ksp->ks_lock = &ssh->lock;
|
||||||
|
ksp->ks_data = NULL;
|
||||||
|
ksp->ks_private = spa;
|
||||||
|
ksp->ks_update = spa_read_history_update;
|
||||||
|
kstat_set_raw_ops(ksp, spa_read_history_headers,
|
||||||
|
spa_read_history_data, spa_read_history_addr);
|
||||||
|
kstat_install(ksp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
spa_read_history_destroy(spa_t *spa)
|
||||||
|
{
|
||||||
|
spa_stats_history_t *ssh = &spa->spa_stats.read_history;
|
||||||
|
spa_read_history_t *srh;
|
||||||
|
kstat_t *ksp;
|
||||||
|
|
||||||
|
ksp = ssh->kstat;
|
||||||
|
if (ksp)
|
||||||
|
kstat_delete(ksp);
|
||||||
|
|
||||||
|
mutex_enter(&ssh->lock);
|
||||||
|
while ((srh = list_remove_head(&ssh->list))) {
|
||||||
|
ssh->size--;
|
||||||
|
kmem_free(srh, sizeof(spa_read_history_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT3U(ssh->size, ==, 0);
|
||||||
|
list_destroy(&ssh->list);
|
||||||
|
mutex_exit(&ssh->lock);
|
||||||
|
|
||||||
|
mutex_destroy(&ssh->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
spa_read_history_add(spa_t *spa, const zbookmark_t *zb, uint32_t aflags)
|
||||||
|
{
|
||||||
|
spa_stats_history_t *ssh = &spa->spa_stats.read_history;
|
||||||
|
spa_read_history_t *srh, *rm;
|
||||||
|
|
||||||
|
ASSERT3P(spa, !=, NULL);
|
||||||
|
ASSERT3P(zb, !=, NULL);
|
||||||
|
|
||||||
|
if (zfs_read_history == 0 && ssh->size == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (zfs_read_history_hits == 0 && (aflags & ARC_CACHED))
|
||||||
|
return;
|
||||||
|
|
||||||
|
srh = kmem_zalloc(sizeof(spa_read_history_t), KM_PUSHPAGE);
|
||||||
|
strlcpy(srh->origin, zb->zb_func, sizeof(srh->origin));
|
||||||
|
strlcpy(srh->comm, getcomm(), sizeof(srh->comm));
|
||||||
|
srh->start = gethrtime();
|
||||||
|
srh->objset = zb->zb_objset;
|
||||||
|
srh->object = zb->zb_object;
|
||||||
|
srh->level = zb->zb_level;
|
||||||
|
srh->blkid = zb->zb_blkid;
|
||||||
|
srh->aflags = aflags;
|
||||||
|
srh->pid = getpid();
|
||||||
|
|
||||||
|
mutex_enter(&ssh->lock);
|
||||||
|
|
||||||
|
srh->uid = ssh->count++;
|
||||||
|
list_insert_head(&ssh->list, srh);
|
||||||
|
ssh->size++;
|
||||||
|
|
||||||
|
while (ssh->size > zfs_read_history) {
|
||||||
|
ssh->size--;
|
||||||
|
rm = list_remove_tail(&ssh->list);
|
||||||
|
kmem_free(rm, sizeof(spa_read_history_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_exit(&ssh->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ==========================================================================
|
||||||
|
* SPA TXG History Routines
|
||||||
|
* ==========================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Txg statistics - Information exported regarding each txg sync
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct spa_txg_history {
|
||||||
|
uint64_t txg; /* txg id */
|
||||||
|
txg_state_t state; /* active txg state */
|
||||||
|
uint64_t nread; /* number of bytes read */
|
||||||
|
uint64_t nwritten; /* number of bytes written */
|
||||||
|
uint64_t reads; /* number of read operations */
|
||||||
|
uint64_t writes; /* number of write operations */
|
||||||
|
uint64_t nreserved; /* number of bytes reserved */
|
||||||
|
hrtime_t times[TXG_STATE_COMMITTED]; /* completion times */
|
||||||
|
list_node_t sth_link;
|
||||||
|
} spa_txg_history_t;
|
||||||
|
|
||||||
|
static int
|
||||||
|
spa_txg_history_headers(char *buf, size_t size)
|
||||||
|
{
|
||||||
|
size = snprintf(buf, size - 1, "%-8s %-16s %-5s %-12s %-12s %-12s "
|
||||||
|
"%-8s %-8s %-12s %-12s %-12s\n", "txg", "birth", "state",
|
||||||
|
"nreserved", "nread", "nwritten", "reads", "writes",
|
||||||
|
"otime", "qtime", "stime");
|
||||||
|
buf[size] = '\0';
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
spa_txg_history_data(char *buf, size_t size, void *data)
|
||||||
|
{
|
||||||
|
spa_txg_history_t *sth = (spa_txg_history_t *)data;
|
||||||
|
uint64_t open = 0, quiesce = 0, sync = 0;
|
||||||
|
char state;
|
||||||
|
|
||||||
|
switch (sth->state) {
|
||||||
|
case TXG_STATE_BIRTH: state = 'B'; break;
|
||||||
|
case TXG_STATE_OPEN: state = 'O'; break;
|
||||||
|
case TXG_STATE_QUIESCED: state = 'Q'; break;
|
||||||
|
case TXG_STATE_SYNCED: state = 'S'; break;
|
||||||
|
case TXG_STATE_COMMITTED: state = 'C'; break;
|
||||||
|
default: state = '?'; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sth->times[TXG_STATE_OPEN])
|
||||||
|
open = sth->times[TXG_STATE_OPEN] -
|
||||||
|
sth->times[TXG_STATE_BIRTH];
|
||||||
|
|
||||||
|
if (sth->times[TXG_STATE_QUIESCED])
|
||||||
|
quiesce = sth->times[TXG_STATE_QUIESCED] -
|
||||||
|
sth->times[TXG_STATE_OPEN];
|
||||||
|
|
||||||
|
if (sth->times[TXG_STATE_SYNCED])
|
||||||
|
sync = sth->times[TXG_STATE_SYNCED] -
|
||||||
|
sth->times[TXG_STATE_QUIESCED];
|
||||||
|
|
||||||
|
size = snprintf(buf, size - 1, "%-8llu %-16llu %-5c %-12llu "
|
||||||
|
"%-12llu %-12llu %-8llu %-8llu %-12llu %-12llu %-12llu\n",
|
||||||
|
(longlong_t)sth->txg, sth->times[TXG_STATE_BIRTH], state,
|
||||||
|
(u_longlong_t)sth->nreserved,
|
||||||
|
(u_longlong_t)sth->nread, (u_longlong_t)sth->nwritten,
|
||||||
|
(u_longlong_t)sth->reads, (u_longlong_t)sth->writes,
|
||||||
|
(u_longlong_t)open, (u_longlong_t)quiesce, (u_longlong_t)sync);
|
||||||
|
buf[size] = '\0';
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate the address for the next spa_stats_history_t entry. The
|
||||||
|
* ssh->lock will be held until ksp->ks_ndata entries are processed.
|
||||||
|
*/
|
||||||
|
static void *
|
||||||
|
spa_txg_history_addr(kstat_t *ksp, loff_t n)
|
||||||
|
{
|
||||||
|
spa_t *spa = ksp->ks_private;
|
||||||
|
spa_stats_history_t *ssh = &spa->spa_stats.txg_history;
|
||||||
|
|
||||||
|
ASSERT(MUTEX_HELD(&ssh->lock));
|
||||||
|
|
||||||
|
if (n == 0)
|
||||||
|
ssh->private = list_tail(&ssh->list);
|
||||||
|
else if (ssh->private)
|
||||||
|
ssh->private = list_prev(&ssh->list, ssh->private);
|
||||||
|
|
||||||
|
return (ssh->private);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When the kstat is written discard all spa_txg_history_t entires. The
|
||||||
|
* ssh->lock will be held until ksp->ks_ndata entries are processed.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
spa_txg_history_update(kstat_t *ksp, int rw)
|
||||||
|
{
|
||||||
|
spa_t *spa = ksp->ks_private;
|
||||||
|
spa_stats_history_t *ssh = &spa->spa_stats.txg_history;
|
||||||
|
|
||||||
|
ASSERT(MUTEX_HELD(&ssh->lock));
|
||||||
|
|
||||||
|
if (rw == KSTAT_WRITE) {
|
||||||
|
spa_txg_history_t *sth;
|
||||||
|
|
||||||
|
while ((sth = list_remove_head(&ssh->list))) {
|
||||||
|
ssh->size--;
|
||||||
|
kmem_free(sth, sizeof(spa_txg_history_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT3U(ssh->size, ==, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ksp->ks_ndata = ssh->size;
|
||||||
|
ksp->ks_data_size = ssh->size * sizeof(spa_txg_history_t);
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
spa_txg_history_init(spa_t *spa)
|
||||||
|
{
|
||||||
|
spa_stats_history_t *ssh = &spa->spa_stats.txg_history;
|
||||||
|
char name[KSTAT_STRLEN];
|
||||||
|
kstat_t *ksp;
|
||||||
|
|
||||||
|
mutex_init(&ssh->lock, NULL, MUTEX_DEFAULT, NULL);
|
||||||
|
list_create(&ssh->list, sizeof (spa_txg_history_t),
|
||||||
|
offsetof(spa_txg_history_t, sth_link));
|
||||||
|
|
||||||
|
ssh->count = 0;
|
||||||
|
ssh->size = 0;
|
||||||
|
ssh->private = NULL;
|
||||||
|
|
||||||
|
(void) snprintf(name, KSTAT_STRLEN, "zfs/%s", spa_name(spa));
|
||||||
|
name[KSTAT_STRLEN-1] = '\0';
|
||||||
|
|
||||||
|
ksp = kstat_create(name, 0, "txgs", "misc",
|
||||||
|
KSTAT_TYPE_RAW, 0, KSTAT_FLAG_VIRTUAL);
|
||||||
|
ssh->kstat = ksp;
|
||||||
|
|
||||||
|
if (ksp) {
|
||||||
|
ksp->ks_lock = &ssh->lock;
|
||||||
|
ksp->ks_data = NULL;
|
||||||
|
ksp->ks_private = spa;
|
||||||
|
ksp->ks_update = spa_txg_history_update;
|
||||||
|
kstat_set_raw_ops(ksp, spa_txg_history_headers,
|
||||||
|
spa_txg_history_data, spa_txg_history_addr);
|
||||||
|
kstat_install(ksp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
spa_txg_history_destroy(spa_t *spa)
|
||||||
|
{
|
||||||
|
spa_stats_history_t *ssh = &spa->spa_stats.txg_history;
|
||||||
|
spa_txg_history_t *sth;
|
||||||
|
kstat_t *ksp;
|
||||||
|
|
||||||
|
ksp = ssh->kstat;
|
||||||
|
if (ksp)
|
||||||
|
kstat_delete(ksp);
|
||||||
|
|
||||||
|
mutex_enter(&ssh->lock);
|
||||||
|
while ((sth = list_remove_head(&ssh->list))) {
|
||||||
|
ssh->size--;
|
||||||
|
kmem_free(sth, sizeof(spa_txg_history_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT3U(ssh->size, ==, 0);
|
||||||
|
list_destroy(&ssh->list);
|
||||||
|
mutex_exit(&ssh->lock);
|
||||||
|
|
||||||
|
mutex_destroy(&ssh->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add a new txg to historical record.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
spa_txg_history_add(spa_t *spa, uint64_t txg)
|
||||||
|
{
|
||||||
|
spa_stats_history_t *ssh = &spa->spa_stats.txg_history;
|
||||||
|
spa_txg_history_t *sth, *rm;
|
||||||
|
|
||||||
|
if (zfs_txg_history == 0 && ssh->size == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
sth = kmem_zalloc(sizeof(spa_txg_history_t), KM_PUSHPAGE);
|
||||||
|
sth->txg = txg;
|
||||||
|
sth->state = TXG_STATE_OPEN;
|
||||||
|
sth->times[TXG_STATE_BIRTH] = gethrtime();
|
||||||
|
|
||||||
|
mutex_enter(&ssh->lock);
|
||||||
|
|
||||||
|
list_insert_head(&ssh->list, sth);
|
||||||
|
ssh->size++;
|
||||||
|
|
||||||
|
while (ssh->size > zfs_txg_history) {
|
||||||
|
ssh->size--;
|
||||||
|
rm = list_remove_tail(&ssh->list);
|
||||||
|
kmem_free(rm, sizeof(spa_txg_history_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_exit(&ssh->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set txg state completion time and increment current state.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
spa_txg_history_set(spa_t *spa, uint64_t txg, txg_state_t completed_state,
|
||||||
|
hrtime_t completed_time)
|
||||||
|
{
|
||||||
|
spa_stats_history_t *ssh = &spa->spa_stats.txg_history;
|
||||||
|
spa_txg_history_t *sth;
|
||||||
|
int error = ENOENT;
|
||||||
|
|
||||||
|
if (zfs_txg_history == 0)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
mutex_enter(&ssh->lock);
|
||||||
|
for (sth = list_head(&ssh->list); sth != NULL;
|
||||||
|
sth = list_next(&ssh->list, sth)) {
|
||||||
|
if (sth->txg == txg) {
|
||||||
|
sth->times[completed_state] = completed_time;
|
||||||
|
sth->state++;
|
||||||
|
error = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mutex_exit(&ssh->lock);
|
||||||
|
|
||||||
|
return (error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set txg IO stats.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
spa_txg_history_set_io(spa_t *spa, uint64_t txg, uint64_t nread,
|
||||||
|
uint64_t nwritten, uint64_t reads, uint64_t writes, uint64_t nreserved)
|
||||||
|
{
|
||||||
|
spa_stats_history_t *ssh = &spa->spa_stats.txg_history;
|
||||||
|
spa_txg_history_t *sth;
|
||||||
|
int error = ENOENT;
|
||||||
|
|
||||||
|
if (zfs_txg_history == 0)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
mutex_enter(&ssh->lock);
|
||||||
|
for (sth = list_head(&ssh->list); sth != NULL;
|
||||||
|
sth = list_next(&ssh->list, sth)) {
|
||||||
|
if (sth->txg == txg) {
|
||||||
|
sth->nread = nread;
|
||||||
|
sth->nwritten = nwritten;
|
||||||
|
sth->reads = reads;
|
||||||
|
sth->writes = writes;
|
||||||
|
sth->nreserved = nreserved;
|
||||||
|
error = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mutex_exit(&ssh->lock);
|
||||||
|
|
||||||
|
return (error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ==========================================================================
|
||||||
|
* SPA TX Assign Histogram Routines
|
||||||
|
* ==========================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Tx statistics - Information exported regarding dmu_tx_assign time.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When the kstat is written zero all buckets. When the kstat is read
|
||||||
|
* count the number of trailing buckets set to zero and update ks_ndata
|
||||||
|
* such that they are not output.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
spa_tx_assign_update(kstat_t *ksp, int rw)
|
||||||
|
{
|
||||||
|
spa_t *spa = ksp->ks_private;
|
||||||
|
spa_stats_history_t *ssh = &spa->spa_stats.tx_assign_histogram;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (rw == KSTAT_WRITE) {
|
||||||
|
for (i = 0; i < ssh->count; i++)
|
||||||
|
((kstat_named_t *)ssh->private)[i].value.ui64 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = ssh->count; i > 0; i--)
|
||||||
|
if (((kstat_named_t *)ssh->private)[i-1].value.ui64 != 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
ksp->ks_ndata = i;
|
||||||
|
ksp->ks_data_size = i * sizeof(kstat_named_t);
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
spa_tx_assign_init(spa_t *spa)
|
||||||
|
{
|
||||||
|
spa_stats_history_t *ssh = &spa->spa_stats.tx_assign_histogram;
|
||||||
|
char name[KSTAT_STRLEN];
|
||||||
|
kstat_named_t *ks;
|
||||||
|
kstat_t *ksp;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
mutex_init(&ssh->lock, NULL, MUTEX_DEFAULT, NULL);
|
||||||
|
|
||||||
|
ssh->count = 42; /* power of two buckets for 1ns to 2,199s */
|
||||||
|
ssh->size = ssh->count * sizeof(kstat_named_t);
|
||||||
|
ssh->private = kmem_alloc(ssh->size, KM_SLEEP);
|
||||||
|
|
||||||
|
(void) snprintf(name, KSTAT_STRLEN, "zfs/%s", spa_name(spa));
|
||||||
|
name[KSTAT_STRLEN-1] = '\0';
|
||||||
|
|
||||||
|
for (i = 0; i < ssh->count; i++) {
|
||||||
|
ks = &((kstat_named_t *)ssh->private)[i];
|
||||||
|
ks->data_type = KSTAT_DATA_UINT64;
|
||||||
|
ks->value.ui64 = 0;
|
||||||
|
(void) snprintf(ks->name, KSTAT_STRLEN, "%llu ns",
|
||||||
|
(u_longlong_t)1 << i);
|
||||||
|
}
|
||||||
|
|
||||||
|
ksp = kstat_create(name, 0, "dmu_tx_assign", "misc",
|
||||||
|
KSTAT_TYPE_NAMED, 0, KSTAT_FLAG_VIRTUAL);
|
||||||
|
ssh->kstat = ksp;
|
||||||
|
|
||||||
|
if (ksp) {
|
||||||
|
ksp->ks_lock = &ssh->lock;
|
||||||
|
ksp->ks_data = ssh->private;
|
||||||
|
ksp->ks_ndata = ssh->count;
|
||||||
|
ksp->ks_data_size = ssh->size;
|
||||||
|
ksp->ks_private = spa;
|
||||||
|
ksp->ks_update = spa_tx_assign_update;
|
||||||
|
kstat_install(ksp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
spa_tx_assign_destroy(spa_t *spa)
|
||||||
|
{
|
||||||
|
spa_stats_history_t *ssh = &spa->spa_stats.tx_assign_histogram;
|
||||||
|
kstat_t *ksp;
|
||||||
|
|
||||||
|
ksp = ssh->kstat;
|
||||||
|
if (ksp)
|
||||||
|
kstat_delete(ksp);
|
||||||
|
|
||||||
|
kmem_free(ssh->private, ssh->size);
|
||||||
|
mutex_destroy(&ssh->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
spa_tx_assign_add_nsecs(spa_t *spa, uint64_t nsecs)
|
||||||
|
{
|
||||||
|
spa_stats_history_t *ssh = &spa->spa_stats.tx_assign_histogram;
|
||||||
|
uint64_t idx = 0;
|
||||||
|
|
||||||
|
while (((1 << idx) < nsecs) && (idx < ssh->size - 1))
|
||||||
|
idx++;
|
||||||
|
|
||||||
|
atomic_inc_64(&((kstat_named_t *)ssh->private)[idx].value.ui64);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
spa_stats_init(spa_t *spa)
|
||||||
|
{
|
||||||
|
spa_read_history_init(spa);
|
||||||
|
spa_txg_history_init(spa);
|
||||||
|
spa_tx_assign_init(spa);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
spa_stats_destroy(spa_t *spa)
|
||||||
|
{
|
||||||
|
spa_tx_assign_destroy(spa);
|
||||||
|
spa_txg_history_destroy(spa);
|
||||||
|
spa_read_history_destroy(spa);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(_KERNEL) && defined(HAVE_SPL)
|
||||||
|
module_param(zfs_read_history, int, 0644);
|
||||||
|
MODULE_PARM_DESC(zfs_read_history, "Historic statistics for the last N reads");
|
||||||
|
|
||||||
|
module_param(zfs_read_history_hits, int, 0644);
|
||||||
|
MODULE_PARM_DESC(zfs_read_history_hits, "Include cache hits in read history");
|
||||||
|
|
||||||
|
module_param(zfs_txg_history, int, 0644);
|
||||||
|
MODULE_PARM_DESC(zfs_txg_history, "Historic statistics for the last N txgs");
|
||||||
|
#endif
|
|
@ -27,11 +27,11 @@
|
||||||
#include <sys/zfs_context.h>
|
#include <sys/zfs_context.h>
|
||||||
#include <sys/txg_impl.h>
|
#include <sys/txg_impl.h>
|
||||||
#include <sys/dmu_impl.h>
|
#include <sys/dmu_impl.h>
|
||||||
|
#include <sys/spa_impl.h>
|
||||||
#include <sys/dmu_tx.h>
|
#include <sys/dmu_tx.h>
|
||||||
#include <sys/dsl_pool.h>
|
#include <sys/dsl_pool.h>
|
||||||
#include <sys/dsl_scan.h>
|
#include <sys/dsl_scan.h>
|
||||||
#include <sys/callb.h>
|
#include <sys/callb.h>
|
||||||
#include <sys/spa_impl.h>
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ZFS Transaction Groups
|
* ZFS Transaction Groups
|
||||||
|
@ -351,8 +351,6 @@ txg_rele_to_sync(txg_handle_t *th)
|
||||||
static void
|
static void
|
||||||
txg_quiesce(dsl_pool_t *dp, uint64_t txg)
|
txg_quiesce(dsl_pool_t *dp, uint64_t txg)
|
||||||
{
|
{
|
||||||
hrtime_t start;
|
|
||||||
txg_history_t *th;
|
|
||||||
tx_state_t *tx = &dp->dp_tx;
|
tx_state_t *tx = &dp->dp_tx;
|
||||||
int g = txg & TXG_MASK;
|
int g = txg & TXG_MASK;
|
||||||
int c;
|
int c;
|
||||||
|
@ -366,6 +364,9 @@ txg_quiesce(dsl_pool_t *dp, uint64_t txg)
|
||||||
ASSERT(txg == tx->tx_open_txg);
|
ASSERT(txg == tx->tx_open_txg);
|
||||||
tx->tx_open_txg++;
|
tx->tx_open_txg++;
|
||||||
|
|
||||||
|
spa_txg_history_set(dp->dp_spa, txg, TXG_STATE_OPEN, gethrtime());
|
||||||
|
spa_txg_history_add(dp->dp_spa, tx->tx_open_txg);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now that we've incremented tx_open_txg, we can let threads
|
* Now that we've incremented tx_open_txg, we can let threads
|
||||||
* enter the next transaction group.
|
* enter the next transaction group.
|
||||||
|
@ -373,20 +374,9 @@ txg_quiesce(dsl_pool_t *dp, uint64_t txg)
|
||||||
for (c = 0; c < max_ncpus; c++)
|
for (c = 0; c < max_ncpus; c++)
|
||||||
mutex_exit(&tx->tx_cpu[c].tc_lock);
|
mutex_exit(&tx->tx_cpu[c].tc_lock);
|
||||||
|
|
||||||
/*
|
|
||||||
* Measure how long the txg was open and replace the kstat.
|
|
||||||
*/
|
|
||||||
th = dsl_pool_txg_history_get(dp, txg);
|
|
||||||
th->th_kstat.open_time = gethrtime() - th->th_kstat.birth;
|
|
||||||
th->th_kstat.state = TXG_STATE_QUIESCING;
|
|
||||||
dsl_pool_txg_history_put(th);
|
|
||||||
dsl_pool_txg_history_add(dp, tx->tx_open_txg);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Quiesce the transaction group by waiting for everyone to txg_exit().
|
* Quiesce the transaction group by waiting for everyone to txg_exit().
|
||||||
*/
|
*/
|
||||||
start = gethrtime();
|
|
||||||
|
|
||||||
for (c = 0; c < max_ncpus; c++) {
|
for (c = 0; c < max_ncpus; c++) {
|
||||||
tx_cpu_t *tc = &tx->tx_cpu[c];
|
tx_cpu_t *tc = &tx->tx_cpu[c];
|
||||||
mutex_enter(&tc->tc_lock);
|
mutex_enter(&tc->tc_lock);
|
||||||
|
@ -395,12 +385,7 @@ txg_quiesce(dsl_pool_t *dp, uint64_t txg)
|
||||||
mutex_exit(&tc->tc_lock);
|
mutex_exit(&tc->tc_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
spa_txg_history_set(dp->dp_spa, txg, TXG_STATE_QUIESCED, gethrtime());
|
||||||
* Measure how long the txg took to quiesce.
|
|
||||||
*/
|
|
||||||
th = dsl_pool_txg_history_get(dp, txg);
|
|
||||||
th->th_kstat.quiesce_time = gethrtime() - start;
|
|
||||||
dsl_pool_txg_history_put(th);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -472,6 +457,7 @@ txg_sync_thread(dsl_pool_t *dp)
|
||||||
spa_t *spa = dp->dp_spa;
|
spa_t *spa = dp->dp_spa;
|
||||||
tx_state_t *tx = &dp->dp_tx;
|
tx_state_t *tx = &dp->dp_tx;
|
||||||
callb_cpr_t cpr;
|
callb_cpr_t cpr;
|
||||||
|
vdev_stat_t *vs1, *vs2;
|
||||||
uint64_t start, delta;
|
uint64_t start, delta;
|
||||||
|
|
||||||
#ifdef _KERNEL
|
#ifdef _KERNEL
|
||||||
|
@ -485,10 +471,11 @@ txg_sync_thread(dsl_pool_t *dp)
|
||||||
|
|
||||||
txg_thread_enter(tx, &cpr);
|
txg_thread_enter(tx, &cpr);
|
||||||
|
|
||||||
|
vs1 = kmem_alloc(sizeof(vdev_stat_t), KM_PUSHPAGE);
|
||||||
|
vs2 = kmem_alloc(sizeof(vdev_stat_t), KM_PUSHPAGE);
|
||||||
|
|
||||||
start = delta = 0;
|
start = delta = 0;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
hrtime_t hrstart;
|
|
||||||
txg_history_t *th;
|
|
||||||
uint64_t timer, timeout;
|
uint64_t timer, timeout;
|
||||||
uint64_t txg;
|
uint64_t txg;
|
||||||
|
|
||||||
|
@ -522,8 +509,13 @@ txg_sync_thread(dsl_pool_t *dp)
|
||||||
txg_thread_wait(tx, &cpr, &tx->tx_quiesce_done_cv, 0);
|
txg_thread_wait(tx, &cpr, &tx->tx_quiesce_done_cv, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tx->tx_exiting)
|
if (tx->tx_exiting) {
|
||||||
|
kmem_free(vs2, sizeof(vdev_stat_t));
|
||||||
|
kmem_free(vs1, sizeof(vdev_stat_t));
|
||||||
txg_thread_exit(tx, &cpr, &tx->tx_sync_thread);
|
txg_thread_exit(tx, &cpr, &tx->tx_sync_thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
vdev_get_stats(spa->spa_root_vdev, vs1);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Consume the quiesced txg which has been handed off to
|
* Consume the quiesced txg which has been handed off to
|
||||||
|
@ -535,17 +527,11 @@ txg_sync_thread(dsl_pool_t *dp)
|
||||||
tx->tx_syncing_txg = txg;
|
tx->tx_syncing_txg = txg;
|
||||||
cv_broadcast(&tx->tx_quiesce_more_cv);
|
cv_broadcast(&tx->tx_quiesce_more_cv);
|
||||||
|
|
||||||
th = dsl_pool_txg_history_get(dp, txg);
|
|
||||||
th->th_kstat.state = TXG_STATE_SYNCING;
|
|
||||||
vdev_get_stats(spa->spa_root_vdev, &th->th_vs1);
|
|
||||||
dsl_pool_txg_history_put(th);
|
|
||||||
|
|
||||||
dprintf("txg=%llu quiesce_txg=%llu sync_txg=%llu\n",
|
dprintf("txg=%llu quiesce_txg=%llu sync_txg=%llu\n",
|
||||||
txg, tx->tx_quiesce_txg_waiting, tx->tx_sync_txg_waiting);
|
txg, tx->tx_quiesce_txg_waiting, tx->tx_sync_txg_waiting);
|
||||||
mutex_exit(&tx->tx_sync_lock);
|
mutex_exit(&tx->tx_sync_lock);
|
||||||
|
|
||||||
start = ddi_get_lbolt();
|
start = ddi_get_lbolt();
|
||||||
hrstart = gethrtime();
|
|
||||||
spa_sync(spa, txg);
|
spa_sync(spa, txg);
|
||||||
delta = ddi_get_lbolt() - start;
|
delta = ddi_get_lbolt() - start;
|
||||||
|
|
||||||
|
@ -559,22 +545,15 @@ txg_sync_thread(dsl_pool_t *dp)
|
||||||
*/
|
*/
|
||||||
txg_dispatch_callbacks(dp, txg);
|
txg_dispatch_callbacks(dp, txg);
|
||||||
|
|
||||||
/*
|
vdev_get_stats(spa->spa_root_vdev, vs2);
|
||||||
* Measure the txg sync time determine the amount of I/O done.
|
spa_txg_history_set_io(spa, txg,
|
||||||
*/
|
vs2->vs_bytes[ZIO_TYPE_READ]-vs1->vs_bytes[ZIO_TYPE_READ],
|
||||||
th = dsl_pool_txg_history_get(dp, txg);
|
vs2->vs_bytes[ZIO_TYPE_WRITE]-vs1->vs_bytes[ZIO_TYPE_WRITE],
|
||||||
vdev_get_stats(spa->spa_root_vdev, &th->th_vs2);
|
vs2->vs_ops[ZIO_TYPE_READ]-vs1->vs_ops[ZIO_TYPE_READ],
|
||||||
th->th_kstat.sync_time = gethrtime() - hrstart;
|
vs2->vs_ops[ZIO_TYPE_WRITE]-vs1->vs_ops[ZIO_TYPE_WRITE],
|
||||||
th->th_kstat.nread = th->th_vs2.vs_bytes[ZIO_TYPE_READ] -
|
dp->dp_space_towrite[txg & TXG_MASK] +
|
||||||
th->th_vs1.vs_bytes[ZIO_TYPE_READ];
|
dp->dp_tempreserved[txg & TXG_MASK] / 2);
|
||||||
th->th_kstat.nwritten = th->th_vs2.vs_bytes[ZIO_TYPE_WRITE] -
|
spa_txg_history_set(spa, txg, TXG_STATE_SYNCED, gethrtime());
|
||||||
th->th_vs1.vs_bytes[ZIO_TYPE_WRITE];
|
|
||||||
th->th_kstat.reads = th->th_vs2.vs_ops[ZIO_TYPE_READ] -
|
|
||||||
th->th_vs1.vs_ops[ZIO_TYPE_READ];
|
|
||||||
th->th_kstat.writes = th->th_vs2.vs_ops[ZIO_TYPE_WRITE] -
|
|
||||||
th->th_vs1.vs_ops[ZIO_TYPE_WRITE];
|
|
||||||
th->th_kstat.state = TXG_STATE_COMMITTED;
|
|
||||||
dsl_pool_txg_history_put(th);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue