1*dea726caSRob Norris# SPDX-License-Identifier: CDDL-1.0 2*dea726caSRob Norris# 3*dea726caSRob Norris# CDDL HEADER START 4*dea726caSRob Norris# 5*dea726caSRob Norris# The contents of this file are subject to the terms of the 6*dea726caSRob Norris# Common Development and Distribution License (the "License"). 7*dea726caSRob Norris# You may not use this file except in compliance with the License. 8*dea726caSRob Norris# 9*dea726caSRob Norris# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*dea726caSRob Norris# or https://opensource.org/licenses/CDDL-1.0. 11*dea726caSRob Norris# See the License for the specific language governing permissions 12*dea726caSRob Norris# and limitations under the License. 13*dea726caSRob Norris# 14*dea726caSRob Norris# When distributing Covered Code, include this CDDL HEADER in each 15*dea726caSRob Norris# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*dea726caSRob Norris# If applicable, add the following below this CDDL HEADER, with the 17*dea726caSRob Norris# fields enclosed by brackets "[]" replaced with your own identifying 18*dea726caSRob Norris# information: Portions Copyright [yyyy] [name of copyright owner] 19*dea726caSRob Norris# 20*dea726caSRob Norris# CDDL HEADER END 21*dea726caSRob Norris# 22*dea726caSRob Norris 23*dea726caSRob Norris# 24*dea726caSRob Norris# Copyright (c) 2025, Klara, Inc. 25*dea726caSRob Norris# Copyright 2025 Edgecast Cloud LLC. 26*dea726caSRob Norris# 27*dea726caSRob Norris 28*dea726caSRob Norris# 29*dea726caSRob Norris# This file provides the following helpers to read kstats from tests. 30*dea726caSRob Norris# 31*dea726caSRob Norris# kstat [-g] <stat> 32*dea726caSRob Norris# kstat_pool [-g] <pool> <stat> 33*dea726caSRob Norris# kstat_dataset [-N] <dataset | pool/objsetid> <stat> 34*dea726caSRob Norris# 35*dea726caSRob Norris# `kstat` and `kstat_pool` return the value of the given <stat>, either 36*dea726caSRob Norris# a global or pool-specific state. 37*dea726caSRob Norris# 38*dea726caSRob Norris# $ kstat dbgmsg 39*dea726caSRob Norris# timestamp message 40*dea726caSRob Norris# 1736848201 spa_history.c:304:spa_history_log_sync(): txg 14734896 ... 41*dea726caSRob Norris# 1736848201 spa_history.c:330:spa_history_log_sync(): ioctl ... 42*dea726caSRob Norris# ... 43*dea726caSRob Norris# 44*dea726caSRob Norris# $ kstat_pool garden state 45*dea726caSRob Norris# ONLINE 46*dea726caSRob Norris# 47*dea726caSRob Norris# To get a single stat within a group or collection, separate the name with 48*dea726caSRob Norris# '.' characters. 49*dea726caSRob Norris# 50*dea726caSRob Norris# $ kstat dbufstats.cache_target_bytes 51*dea726caSRob Norris# 3215780693 52*dea726caSRob Norris# 53*dea726caSRob Norris# $ kstat_pool crayon iostats.arc_read_bytes 54*dea726caSRob Norris# 253671670784 55*dea726caSRob Norris# 56*dea726caSRob Norris# -g is "group" mode. If the kstat is a group or collection, all stats in that 57*dea726caSRob Norris# group are returned, one stat per line, key and value separated by a space. 58*dea726caSRob Norris# 59*dea726caSRob Norris# $ kstat -g dbufstats 60*dea726caSRob Norris# cache_count 1792 61*dea726caSRob Norris# cache_size_bytes 87720376 62*dea726caSRob Norris# cache_size_bytes_max 305187768 63*dea726caSRob Norris# cache_target_bytes 97668555 64*dea726caSRob Norris# ... 65*dea726caSRob Norris# 66*dea726caSRob Norris# $ kstat_pool -g crayon iostats 67*dea726caSRob Norris# trim_extents_written 0 68*dea726caSRob Norris# trim_bytes_written 0 69*dea726caSRob Norris# trim_extents_skipped 0 70*dea726caSRob Norris# trim_bytes_skipped 0 71*dea726caSRob Norris# ... 72*dea726caSRob Norris# 73*dea726caSRob Norris# `kstat_dataset` accesses the per-dataset group kstat. The dataset can be 74*dea726caSRob Norris# specified by name: 75*dea726caSRob Norris# 76*dea726caSRob Norris# $ kstat_dataset crayon/home/robn nunlinks 77*dea726caSRob Norris# 2628514 78*dea726caSRob Norris# 79*dea726caSRob Norris# or, with the -N switch, as <pool>/<objsetID>: 80*dea726caSRob Norris# 81*dea726caSRob Norris# $ kstat_dataset -N crayon/7 writes 82*dea726caSRob Norris# 125135 83*dea726caSRob Norris# 84*dea726caSRob Norris 85*dea726caSRob Norris#################### 86*dea726caSRob Norris# Public interface 87*dea726caSRob Norris 88*dea726caSRob Norris# 89*dea726caSRob Norris# kstat [-g] <stat> 90*dea726caSRob Norris# 91*dea726caSRob Norrisfunction kstat 92*dea726caSRob Norris{ 93*dea726caSRob Norris typeset -i want_group=0 94*dea726caSRob Norris 95*dea726caSRob Norris OPTIND=1 96*dea726caSRob Norris while getopts "g" opt ; do 97*dea726caSRob Norris case $opt in 98*dea726caSRob Norris 'g') want_group=1 ;; 99*dea726caSRob Norris *) log_fail "kstat: invalid option '$opt'" ;; 100*dea726caSRob Norris esac 101*dea726caSRob Norris done 102*dea726caSRob Norris shift $(expr $OPTIND - 1) 103*dea726caSRob Norris 104*dea726caSRob Norris typeset stat=$1 105*dea726caSRob Norris 106*dea726caSRob Norris $_kstat_os 'global' '' "$stat" $want_group 107*dea726caSRob Norris} 108*dea726caSRob Norris 109*dea726caSRob Norris# 110*dea726caSRob Norris# kstat_pool [-g] <pool> <stat> 111*dea726caSRob Norris# 112*dea726caSRob Norrisfunction kstat_pool 113*dea726caSRob Norris{ 114*dea726caSRob Norris typeset -i want_group=0 115*dea726caSRob Norris 116*dea726caSRob Norris OPTIND=1 117*dea726caSRob Norris while getopts "g" opt ; do 118*dea726caSRob Norris case $opt in 119*dea726caSRob Norris 'g') want_group=1 ;; 120*dea726caSRob Norris *) log_fail "kstat_pool: invalid option '$opt'" ;; 121*dea726caSRob Norris esac 122*dea726caSRob Norris done 123*dea726caSRob Norris shift $(expr $OPTIND - 1) 124*dea726caSRob Norris 125*dea726caSRob Norris typeset pool=$1 126*dea726caSRob Norris typeset stat=$2 127*dea726caSRob Norris 128*dea726caSRob Norris $_kstat_os 'pool' "$pool" "$stat" $want_group 129*dea726caSRob Norris} 130*dea726caSRob Norris 131*dea726caSRob Norris# 132*dea726caSRob Norris# kstat_dataset [-N] <dataset | pool/objsetid> <stat> 133*dea726caSRob Norris# 134*dea726caSRob Norrisfunction kstat_dataset 135*dea726caSRob Norris{ 136*dea726caSRob Norris typeset -i opt_objsetid=0 137*dea726caSRob Norris 138*dea726caSRob Norris OPTIND=1 139*dea726caSRob Norris while getopts "N" opt ; do 140*dea726caSRob Norris case $opt in 141*dea726caSRob Norris 'N') opt_objsetid=1 ;; 142*dea726caSRob Norris *) log_fail "kstat_dataset: invalid option '$opt'" ;; 143*dea726caSRob Norris esac 144*dea726caSRob Norris done 145*dea726caSRob Norris shift $(expr $OPTIND - 1) 146*dea726caSRob Norris 147*dea726caSRob Norris typeset dsarg=$1 148*dea726caSRob Norris typeset stat=$2 149*dea726caSRob Norris 150*dea726caSRob Norris if [[ $opt_objsetid == 0 ]] ; then 151*dea726caSRob Norris typeset pool="${dsarg%%/*}" # clear first / -> end 152*dea726caSRob Norris typeset objsetid=$($_resolve_dsname_os "$pool" "$dsarg") 153*dea726caSRob Norris if [[ -z "$objsetid" ]] ; then 154*dea726caSRob Norris log_fail "kstat_dataset: dataset not found: $dsarg" 155*dea726caSRob Norris fi 156*dea726caSRob Norris dsarg="$pool/$objsetid" 157*dea726caSRob Norris fi 158*dea726caSRob Norris 159*dea726caSRob Norris $_kstat_os 'dataset' "$dsarg" "$stat" 0 160*dea726caSRob Norris} 161*dea726caSRob Norris 162*dea726caSRob Norris#################### 163*dea726caSRob Norris# Platform-specific interface 164*dea726caSRob Norris 165*dea726caSRob Norris# 166*dea726caSRob Norris# Implementation notes 167*dea726caSRob Norris# 168*dea726caSRob Norris# There's not a lot of uniformity between platforms, so I've written to a rough 169*dea726caSRob Norris# imagined model that seems to fit the majority of OpenZFS kstats. 170*dea726caSRob Norris# 171*dea726caSRob Norris# The main platform entry points look like this: 172*dea726caSRob Norris# 173*dea726caSRob Norris# _kstat_freebsd <scope> <object> <stat> <want_group> 174*dea726caSRob Norris# _kstat_illumos <scope> <object> <stat> <want_group> 175*dea726caSRob Norris# _kstat_linux <scope> <object> <stat> <want_group> 176*dea726caSRob Norris# 177*dea726caSRob Norris# - scope: one of 'global', 'pool', 'dataset'. The "kind" of object the kstat 178*dea726caSRob Norris# is attached to. 179*dea726caSRob Norris# - object: name of the scoped object 180*dea726caSRob Norris# global: empty string 181*dea726caSRob Norris# pool: pool name 182*dea726caSRob Norris# dataset: <pool>/<objsetId> pair 183*dea726caSRob Norris# - stat: kstat name to get 184*dea726caSRob Norris# - want_group: 0 to get the single value for the kstat, 1 to treat the kstat 185*dea726caSRob Norris# as a group and get all the stat names+values under it. group 186*dea726caSRob Norris# kstats cannot have values, and stat kstats cannot have 187*dea726caSRob Norris# children (by definition) 188*dea726caSRob Norris# 189*dea726caSRob Norris# Stat values can have multiple lines, so be prepared for those. 190*dea726caSRob Norris# 191*dea726caSRob Norris# These functions either succeed and produce the requested output, or call 192*dea726caSRob Norris# log_fail. They should never output empty, or 0, or anything else. 193*dea726caSRob Norris# 194*dea726caSRob Norris# Output: 195*dea726caSRob Norris# 196*dea726caSRob Norris# - want_group=0: the single stat value, followed by newline 197*dea726caSRob Norris# - want_group=1: One stat per line, <name><SP><value><newline> 198*dea726caSRob Norris# 199*dea726caSRob Norris 200*dea726caSRob Norris# 201*dea726caSRob Norris# To support kstat_dataset(), platforms also need to provide a dataset 202*dea726caSRob Norris# name->object id resolver function. 203*dea726caSRob Norris# 204*dea726caSRob Norris# _resolve_dsname_freebsd <pool> <dsname> 205*dea726caSRob Norris# _resolve_dsname_illumos <pool> <dsname> 206*dea726caSRob Norris# _resolve_dsname_linux <pool> <dsname> 207*dea726caSRob Norris# 208*dea726caSRob Norris# - pool: pool name. always the first part of the dataset name 209*dea726caSRob Norris# - dsname: dataset name, in the standard <pool>/<some>/<dataset> format. 210*dea726caSRob Norris# 211*dea726caSRob Norris# Output is <objsetID>. objsetID is a decimal integer, > 0 212*dea726caSRob Norris# 213*dea726caSRob Norris 214*dea726caSRob Norris#################### 215*dea726caSRob Norris# FreeBSD 216*dea726caSRob Norris 217*dea726caSRob Norris# 218*dea726caSRob Norris# All kstats are accessed through sysctl. We model "groups" as interior nodes 219*dea726caSRob Norris# in the stat tree, which are normally opaque. Because sysctl has no filtering 220*dea726caSRob Norris# options, and requesting any node produces all nodes below it, we have to 221*dea726caSRob Norris# always get the name and value, and then consider the output to understand 222*dea726caSRob Norris# if we got a group or a single stat, and post-process accordingly. 223*dea726caSRob Norris# 224*dea726caSRob Norris# Scopes are mostly mapped directly to known locations in the tree, but there 225*dea726caSRob Norris# are a handful of stats that are out of position, so we need to adjust. 226*dea726caSRob Norris# 227*dea726caSRob Norris 228*dea726caSRob Norris# 229*dea726caSRob Norris# _kstat_freebsd <scope> <object> <stat> <want_group> 230*dea726caSRob Norris# 231*dea726caSRob Norrisfunction _kstat_freebsd 232*dea726caSRob Norris{ 233*dea726caSRob Norris typeset scope=$1 234*dea726caSRob Norris typeset obj=$2 235*dea726caSRob Norris typeset stat=$3 236*dea726caSRob Norris typeset -i want_group=$4 237*dea726caSRob Norris 238*dea726caSRob Norris typeset oid="" 239*dea726caSRob Norris case "$scope" in 240*dea726caSRob Norris global) 241*dea726caSRob Norris oid="kstat.zfs.misc.$stat" 242*dea726caSRob Norris ;; 243*dea726caSRob Norris pool) 244*dea726caSRob Norris # For reasons unknown, the "multihost", "txgs" and "reads" 245*dea726caSRob Norris # pool-specific kstats are directly under kstat.zfs.<pool>, 246*dea726caSRob Norris # rather than kstat.zfs.<pool>.misc like the other pool kstats. 247*dea726caSRob Norris # Adjust for that here. 248*dea726caSRob Norris case "$stat" in 249*dea726caSRob Norris multihost|txgs|reads) 250*dea726caSRob Norris oid="kstat.zfs.$obj.$stat" 251*dea726caSRob Norris ;; 252*dea726caSRob Norris *) 253*dea726caSRob Norris oid="kstat.zfs.$obj.misc.$stat" 254*dea726caSRob Norris ;; 255*dea726caSRob Norris esac 256*dea726caSRob Norris ;; 257*dea726caSRob Norris dataset) 258*dea726caSRob Norris typeset pool="" 259*dea726caSRob Norris typeset -i objsetid=0 260*dea726caSRob Norris _split_pool_objsetid $obj pool objsetid 261*dea726caSRob Norris oid=$(printf 'kstat.zfs.%s.dataset.objset-0x%x.%s' \ 262*dea726caSRob Norris $pool $objsetid $stat) 263*dea726caSRob Norris ;; 264*dea726caSRob Norris esac 265*dea726caSRob Norris 266*dea726caSRob Norris # Calling sysctl on a "group" node will return everything under that 267*dea726caSRob Norris # node, so we have to inspect the first line to make sure we are 268*dea726caSRob Norris # getting back what we expect. For a single value, the key will have 269*dea726caSRob Norris # the name we requested, while for a group, the key will not have the 270*dea726caSRob Norris # name (group nodes are "opaque", not returned by sysctl by default. 271*dea726caSRob Norris 272*dea726caSRob Norris if [[ $want_group == 0 ]] ; then 273*dea726caSRob Norris sysctl -e "$oid" | awk -v oid="$oid" -v oidre="^$oid=" ' 274*dea726caSRob Norris NR == 1 && $0 !~ oidre { exit 1 } 275*dea726caSRob Norris NR == 1 { print substr($0, length(oid)+2) ; next } 276*dea726caSRob Norris { print } 277*dea726caSRob Norris ' 278*dea726caSRob Norris else 279*dea726caSRob Norris sysctl -e "$oid" | awk -v oid="$oid" -v oidre="^$oid=" ' 280*dea726caSRob Norris NR == 1 && $0 ~ oidre { exit 2 } 281*dea726caSRob Norris { 282*dea726caSRob Norris sub("^" oid "\.", "") 283*dea726caSRob Norris sub("=", " ") 284*dea726caSRob Norris print 285*dea726caSRob Norris } 286*dea726caSRob Norris ' 287*dea726caSRob Norris fi 288*dea726caSRob Norris 289*dea726caSRob Norris typeset -i err=$? 290*dea726caSRob Norris case $err in 291*dea726caSRob Norris 0) return ;; 292*dea726caSRob Norris 1) log_fail "kstat: can't get value for group kstat: $oid" ;; 293*dea726caSRob Norris 2) log_fail "kstat: not a group kstat: $oid" ;; 294*dea726caSRob Norris esac 295*dea726caSRob Norris 296*dea726caSRob Norris log_fail "kstat: unknown error: $oid" 297*dea726caSRob Norris} 298*dea726caSRob Norris 299*dea726caSRob Norris# 300*dea726caSRob Norris# _resolve_dsname_freebsd <pool> <dsname> 301*dea726caSRob Norris# 302*dea726caSRob Norrisfunction _resolve_dsname_freebsd 303*dea726caSRob Norris{ 304*dea726caSRob Norris # we're searching for: 305*dea726caSRob Norris # 306*dea726caSRob Norris # kstat.zfs.shed.dataset.objset-0x8087.dataset_name: shed/poudriere 307*dea726caSRob Norris # 308*dea726caSRob Norris # We split on '.', then get the hex objsetid from field 5. 309*dea726caSRob Norris # 310*dea726caSRob Norris # We convert hex to decimal in the shell because there isn't a _simple_ 311*dea726caSRob Norris # portable way to do it in awk and this code is already too intense to 312*dea726caSRob Norris # do it a complicated way. 313*dea726caSRob Norris typeset pool=$1 314*dea726caSRob Norris typeset dsname=$2 315*dea726caSRob Norris sysctl -e kstat.zfs.$pool | \ 316*dea726caSRob Norris awk -F '.' -v dsnamere="=$dsname$" ' 317*dea726caSRob Norris /\.objset-0x[0-9a-f]+\.dataset_name=/ && $6 ~ dsnamere { 318*dea726caSRob Norris print substr($5, 8) 319*dea726caSRob Norris exit 320*dea726caSRob Norris } 321*dea726caSRob Norris ' | xargs printf %d 322*dea726caSRob Norris} 323*dea726caSRob Norris 324*dea726caSRob Norris#################### 325*dea726caSRob Norris# Linux 326*dea726caSRob Norris 327*dea726caSRob Norris# 328*dea726caSRob Norris# kstats all live under /proc/spl/kstat/zfs. They have a flat structure: global 329*dea726caSRob Norris# at top-level, pool in a directory, and dataset in a objset- file inside the 330*dea726caSRob Norris# pool dir. 331*dea726caSRob Norris# 332*dea726caSRob Norris# Groups are challenge. A single stat can be the entire text of a file, or 333*dea726caSRob Norris# a single line that must be extracted from a "group" file. The only way to 334*dea726caSRob Norris# recognise a group from the outside is to look for its header. This naturally 335*dea726caSRob Norris# breaks if a raw file had a matching header, or if a group file chooses to 336*dea726caSRob Norris# hid its header. Fortunately OpenZFS does none of these things at the moment. 337*dea726caSRob Norris# 338*dea726caSRob Norris 339*dea726caSRob Norris# 340*dea726caSRob Norris# _kstat_linux <scope> <object> <stat> <want_group> 341*dea726caSRob Norris# 342*dea726caSRob Norrisfunction _kstat_linux 343*dea726caSRob Norris{ 344*dea726caSRob Norris typeset scope=$1 345*dea726caSRob Norris typeset obj=$2 346*dea726caSRob Norris typeset stat=$3 347*dea726caSRob Norris typeset -i want_group=$4 348*dea726caSRob Norris 349*dea726caSRob Norris typeset singlestat="" 350*dea726caSRob Norris 351*dea726caSRob Norris if [[ $scope == 'dataset' ]] ; then 352*dea726caSRob Norris typeset pool="" 353*dea726caSRob Norris typeset -i objsetid=0 354*dea726caSRob Norris _split_pool_objsetid $obj pool objsetid 355*dea726caSRob Norris stat=$(printf 'objset-0x%x.%s' $objsetid $stat) 356*dea726caSRob Norris obj=$pool 357*dea726caSRob Norris scope='pool' 358*dea726caSRob Norris fi 359*dea726caSRob Norris 360*dea726caSRob Norris typeset path="" 361*dea726caSRob Norris if [[ $scope == 'global' ]] ; then 362*dea726caSRob Norris path="/proc/spl/kstat/zfs/$stat" 363*dea726caSRob Norris else 364*dea726caSRob Norris path="/proc/spl/kstat/zfs/$obj/$stat" 365*dea726caSRob Norris fi 366*dea726caSRob Norris 367*dea726caSRob Norris if [[ ! -e "$path" && $want_group -eq 0 ]] ; then 368*dea726caSRob Norris # This single stat doesn't have its own file, but the wanted 369*dea726caSRob Norris # stat could be in a group kstat file, which we now need to 370*dea726caSRob Norris # find. To do this, we split a single stat name into two parts: 371*dea726caSRob Norris # the file that would contain the stat, and the key within that 372*dea726caSRob Norris # file to match on. This works by converting all bar the last 373*dea726caSRob Norris # '.' separator to '/', then splitting on the remaining '.' 374*dea726caSRob Norris # separator. If there are no '.' separators, the second arg 375*dea726caSRob Norris # returned will be empty. 376*dea726caSRob Norris # 377*dea726caSRob Norris # foo -> (foo) 378*dea726caSRob Norris # foo.bar -> (foo, bar) 379*dea726caSRob Norris # foo.bar.baz -> (foo/bar, baz) 380*dea726caSRob Norris # foo.bar.baz.quux -> (foo/bar/baz, quux) 381*dea726caSRob Norris # 382*dea726caSRob Norris # This is how we will target single stats within a larger NAMED 383*dea726caSRob Norris # kstat file, eg dbufstats.cache_target_bytes. 384*dea726caSRob Norris typeset -a split=($(echo "$stat" | \ 385*dea726caSRob Norris sed -E 's/^(.+)\.([^\.]+)$/\1 \2/ ; s/\./\//g')) 386*dea726caSRob Norris typeset statfile=${split[0]} 387*dea726caSRob Norris singlestat=${split[1]:-""} 388*dea726caSRob Norris 389*dea726caSRob Norris if [[ $scope == 'global' ]] ; then 390*dea726caSRob Norris path="/proc/spl/kstat/zfs/$statfile" 391*dea726caSRob Norris else 392*dea726caSRob Norris path="/proc/spl/kstat/zfs/$obj/$statfile" 393*dea726caSRob Norris fi 394*dea726caSRob Norris fi 395*dea726caSRob Norris if [[ ! -r "$path" ]] ; then 396*dea726caSRob Norris log_fail "kstat: can't read $path" 397*dea726caSRob Norris fi 398*dea726caSRob Norris 399*dea726caSRob Norris if [[ $want_group == 1 ]] ; then 400*dea726caSRob Norris # "group" (NAMED) kstats on Linux start: 401*dea726caSRob Norris # 402*dea726caSRob Norris # $ cat /proc/spl/kstat/zfs/crayon/iostats 403*dea726caSRob Norris # 70 1 0x01 26 7072 8577844978 661416318663496 404*dea726caSRob Norris # name type data 405*dea726caSRob Norris # trim_extents_written 4 0 406*dea726caSRob Norris # trim_bytes_written 4 0 407*dea726caSRob Norris # 408*dea726caSRob Norris # The second value on the first row is the ks_type. Group 409*dea726caSRob Norris # mode only works for type 1, KSTAT_TYPE_NAMED. So we check 410*dea726caSRob Norris # for that, and eject if it's the wrong type. Otherwise, we 411*dea726caSRob Norris # skip the header row and process the values. 412*dea726caSRob Norris awk ' 413*dea726caSRob Norris NR == 1 && ! /^[0-9]+ 1 / { exit 2 } 414*dea726caSRob Norris NR < 3 { next } 415*dea726caSRob Norris { print $1 " " $NF } 416*dea726caSRob Norris ' "$path" 417*dea726caSRob Norris elif [[ -n $singlestat ]] ; then 418*dea726caSRob Norris # single stat. must be a single line within a group stat, so 419*dea726caSRob Norris # we look for the header again as above. 420*dea726caSRob Norris awk -v singlestat="$singlestat" \ 421*dea726caSRob Norris -v singlestatre="^$singlestat " ' 422*dea726caSRob Norris NR == 1 && /^[0-9]+ [^1] / { exit 2 } 423*dea726caSRob Norris NR < 3 { next } 424*dea726caSRob Norris $0 ~ singlestatre { print $NF ; exit 0 } 425*dea726caSRob Norris ENDFILE { exit 3 } 426*dea726caSRob Norris ' "$path" 427*dea726caSRob Norris else 428*dea726caSRob Norris # raw stat. dump contents, exclude group stats 429*dea726caSRob Norris awk ' 430*dea726caSRob Norris NR == 1 && /^[0-9]+ 1 / { exit 1 } 431*dea726caSRob Norris { print } 432*dea726caSRob Norris ' "$path" 433*dea726caSRob Norris fi 434*dea726caSRob Norris 435*dea726caSRob Norris typeset -i err=$? 436*dea726caSRob Norris case $err in 437*dea726caSRob Norris 0) return ;; 438*dea726caSRob Norris 1) log_fail "kstat: can't get value for group kstat: $path" ;; 439*dea726caSRob Norris 2) log_fail "kstat: not a group kstat: $path" ;; 440*dea726caSRob Norris 3) log_fail "kstat: stat not found in group: $path $singlestat" ;; 441*dea726caSRob Norris esac 442*dea726caSRob Norris 443*dea726caSRob Norris log_fail "kstat: unknown error: $path" 444*dea726caSRob Norris} 445*dea726caSRob Norris 446*dea726caSRob Norris# 447*dea726caSRob Norris# _resolve_dsname_linux <pool> <dsname> 448*dea726caSRob Norris# 449*dea726caSRob Norrisfunction _resolve_dsname_linux 450*dea726caSRob Norris{ 451*dea726caSRob Norris # We look inside all: 452*dea726caSRob Norris # 453*dea726caSRob Norris # /proc/spl/kstat/zfs/crayon/objset-0x113 454*dea726caSRob Norris # 455*dea726caSRob Norris # and check the dataset_name field inside. If we get a match, we split 456*dea726caSRob Norris # the filename on /, then extract the hex objsetid. 457*dea726caSRob Norris # 458*dea726caSRob Norris # We convert hex to decimal in the shell because there isn't a _simple_ 459*dea726caSRob Norris # portable way to do it in awk and this code is already too intense to 460*dea726caSRob Norris # do it a complicated way. 461*dea726caSRob Norris typeset pool=$1 462*dea726caSRob Norris typeset dsname=$2 463*dea726caSRob Norris awk -v dsname="$dsname" ' 464*dea726caSRob Norris $1 == "dataset_name" && $3 == dsname { 465*dea726caSRob Norris split(FILENAME, a, "/") 466*dea726caSRob Norris print substr(a[7], 8) 467*dea726caSRob Norris exit 468*dea726caSRob Norris } 469*dea726caSRob Norris ' /proc/spl/kstat/zfs/$pool/objset-0x* | xargs printf %d 470*dea726caSRob Norris} 471*dea726caSRob Norris 472*dea726caSRob Norris#################### 473*dea726caSRob Norris 474*dea726caSRob Norris# 475*dea726caSRob Norris# _split_pool_objsetid <obj> <*pool> <*objsetid> 476*dea726caSRob Norris# 477*dea726caSRob Norris# Splits pool/objsetId string in <obj> and fills <pool> and <objsetid>. 478*dea726caSRob Norris# 479*dea726caSRob Norrisfunction _split_pool_objsetid 480*dea726caSRob Norris{ 481*dea726caSRob Norris typeset obj=$1 482*dea726caSRob Norris typeset -n pool=$2 483*dea726caSRob Norris typeset -n objsetid=$3 484*dea726caSRob Norris 485*dea726caSRob Norris pool="${obj%%/*}" # clear first / -> end 486*dea726caSRob Norris typeset osidarg="${obj#*/}" # clear start -> first / 487*dea726caSRob Norris 488*dea726caSRob Norris # ensure objsetid arg does not contain a /. we're about to convert it, 489*dea726caSRob Norris # but ksh will treat it as an expression, and a / will give a 490*dea726caSRob Norris # divide-by-zero 491*dea726caSRob Norris if [[ "${osidarg%%/*}" != "$osidarg" ]] ; then 492*dea726caSRob Norris log_fail "kstat: invalid objsetid: $osidarg" 493*dea726caSRob Norris fi 494*dea726caSRob Norris 495*dea726caSRob Norris typeset -i id=$osidarg 496*dea726caSRob Norris if [[ $id -le 0 ]] ; then 497*dea726caSRob Norris log_fail "kstat: invalid objsetid: $osidarg" 498*dea726caSRob Norris fi 499*dea726caSRob Norris objsetid=$id 500*dea726caSRob Norris} 501*dea726caSRob Norris 502*dea726caSRob Norris#################### 503*dea726caSRob Norris# illumos 504*dea726caSRob Norris 505*dea726caSRob Norris# 506*dea726caSRob Norris# _kstat_illumos <scope> <object> <stat> <want_group> 507*dea726caSRob Norris# 508*dea726caSRob Norrisfunction _kstat_illumos 509*dea726caSRob Norris{ 510*dea726caSRob Norris typeset scope=$1 511*dea726caSRob Norris typeset obj=$2 512*dea726caSRob Norris typeset stat=${3/\./:} 513*dea726caSRob Norris typeset -i want_group=$4 514*dea726caSRob Norris 515*dea726caSRob Norris typeset oid="" 516*dea726caSRob Norris case "$scope" in 517*dea726caSRob Norris global) 518*dea726caSRob Norris oid="zfs::$stat" 519*dea726caSRob Norris ;; 520*dea726caSRob Norris pool) 521*dea726caSRob Norris oid="zfs::$obj:$stat" 522*dea726caSRob Norris ;; 523*dea726caSRob Norris dataset) 524*dea726caSRob Norris log_fail "kstat: dataset kstats are not implemented" 525*dea726caSRob Norris ;; 526*dea726caSRob Norris esac 527*dea726caSRob Norris command kstat -pV $oid 528*dea726caSRob Norris} 529*dea726caSRob Norris 530*dea726caSRob Norris# 531*dea726caSRob Norris# _resolve_dsname_illumos <pool> <dsname> 532*dea726caSRob Norris# 533*dea726caSRob Norrisfunction _resolve_dsname_illumos 534*dea726caSRob Norris{ 535*dea726caSRob Norris typeset pool=$1 536*dea726caSRob Norris typeset dsname=$2 537*dea726caSRob Norris 538*dea726caSRob Norris # we do not have per dataset kstats 539*dea726caSRob Norris} 540*dea726caSRob Norris 541*dea726caSRob Norris#################### 542*dea726caSRob Norris 543*dea726caSRob Norris# 544*dea726caSRob Norris# Per-platform function selection. 545*dea726caSRob Norris# 546*dea726caSRob Norris# To avoid needing platform check throughout, we store the names of the 547*dea726caSRob Norris# platform functions and call through them. 548*dea726caSRob Norris# 549*dea726caSRob Norrisif is_freebsd ; then 550*dea726caSRob Norris _kstat_os='_kstat_freebsd' 551*dea726caSRob Norris _resolve_dsname_os='_resolve_dsname_freebsd' 552*dea726caSRob Norriselif is_linux ; then 553*dea726caSRob Norris _kstat_os='_kstat_linux' 554*dea726caSRob Norris _resolve_dsname_os='_resolve_dsname_linux' 555*dea726caSRob Norriselif is_illumos ; then 556*dea726caSRob Norris _kstat_os='_kstat_illumos' 557*dea726caSRob Norris _resolve_dsname_os='_resolve_dsname_illumos' 558*dea726caSRob Norriselse 559*dea726caSRob Norris _kstat_os='_kstat_unknown_platform_implement_me' 560*dea726caSRob Norris _resolve_dsname_os='_resolve_dsname_unknown_platform_implement_me' 561*dea726caSRob Norrisfi 562