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