* [pbs-devel] [PATCH proxmox-backup] fix #3743: extract zfs dataset io stats from /proc/spl/kstat/zfs/POOL/objset-*
@ 2021-12-15 15:16 Dominik Csapak
0 siblings, 0 replies; only message in thread
From: Dominik Csapak @ 2021-12-15 15:16 UTC (permalink / raw)
To: pbs-devel
Recently, ZFS removed the pool global io stats from
/proc/spl/kstat/zfs/POOL/io with no replacement.
To gather stats about the datastores, access now the objset specific
entries there. To be able to make that efficient, cache a map of
dataset <-> obset ids, so that we do not have to parse all files each time.
We update the cache each time we try to get the info for a dataset
where we do not have a mapping.
We cannot update it on datastore add/remove since that happens in the
proxmox-backup daemon, while we need the info here in proxmox-backup-proxy.
Sadly with this we lose the io wait metric, but it seems that this is no
longer tracked in zfs at all, so nothing we can do for that.
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
the iowait graph is still shown by default, did not have the time to
come up with a mechanism to hide that for zfs backed datastores yet, but
would be independet of this patch anyway
src/bin/proxmox-backup-proxy.rs | 21 +++---
src/tools/disks/zfs.rs | 123 +++++++++++++++++++++++++++++---
2 files changed, 125 insertions(+), 19 deletions(-)
diff --git a/src/bin/proxmox-backup-proxy.rs b/src/bin/proxmox-backup-proxy.rs
index 07a53687..b6be080c 100644
--- a/src/bin/proxmox-backup-proxy.rs
+++ b/src/bin/proxmox-backup-proxy.rs
@@ -62,8 +62,7 @@ use proxmox_backup::tools::{
PROXMOX_BACKUP_TCP_KEEPALIVE_TIME,
disks::{
DiskManage,
- zfs_pool_stats,
- get_pool_from_dataset,
+ zfs_dataset_stats,
},
};
@@ -1071,16 +1070,16 @@ fn gather_disk_stats(disk_manager: Arc<DiskManage>, path: &Path, rrd_prefix: &st
Ok(None) => {},
Ok(Some((fs_type, device, source))) => {
let mut device_stat = None;
- match fs_type.as_str() {
- "zfs" => {
- if let Some(source) = source {
- let pool = get_pool_from_dataset(&source).unwrap_or(&source);
- match zfs_pool_stats(pool) {
- Ok(stat) => device_stat = stat,
- Err(err) => eprintln!("zfs_pool_stats({:?}) failed - {}", pool, err),
- }
+ match (fs_type.as_str(), source) {
+ ("zfs", Some(source)) => match source.into_string() {
+ Ok(dataset) => match zfs_dataset_stats(&dataset) {
+ Ok(stat) => device_stat = Some(stat),
+ Err(err) => eprintln!("zfs_dataset_stats({:?}) failed - {}", dataset, err),
+ },
+ Err(source) => {
+ eprintln!("zfs_pool_stats({:?}) failed - invalid characters", source)
}
- }
+ },
_ => {
if let Ok(disk) = disk_manager.clone().disk_by_dev_num(device.into_dev_t()) {
match disk.read_stat() {
diff --git a/src/tools/disks/zfs.rs b/src/tools/disks/zfs.rs
index 2f18919d..522a04b5 100644
--- a/src/tools/disks/zfs.rs
+++ b/src/tools/disks/zfs.rs
@@ -1,10 +1,13 @@
use std::path::PathBuf;
use std::collections::HashSet;
use std::os::unix::fs::MetadataExt;
+use std::sync::{Arc, Mutex};
use anyhow::{bail, Error};
use lazy_static::lazy_static;
+use proxmox_schema::const_regex;
+
use super::*;
lazy_static!{
@@ -16,15 +19,12 @@ lazy_static!{
};
}
-/// returns pool from dataset path of the form 'rpool/ROOT/pbs-1'
-pub fn get_pool_from_dataset(dataset: &OsStr) -> Option<&OsStr> {
- if let Some(dataset) = dataset.to_str() {
- if let Some(idx) = dataset.find('/') {
- return Some(&dataset[0..idx].as_ref());
- }
+fn get_pool_from_dataset(dataset: &str) -> &str {
+ if let Some(idx) = dataset.find('/') {
+ &dataset[0..idx].as_ref()
+ } else {
+ dataset.as_ref()
}
-
- None
}
/// Returns kernel IO-stats for zfs pools
@@ -97,3 +97,110 @@ pub fn zfs_devices(
Ok(device_set)
}
+const ZFS_KSTAT_BASE_PATH: &str = "/proc/spl/kstat/zfs";
+const_regex! {
+ OBJSET_REGEX = r"^objset-0x[a-fA-F0-9]+$";
+}
+
+lazy_static::lazy_static! {
+ pub static ref ZFS_DATASET_OBJSET_MAP: Arc<Mutex<HashMap<String, (String, String)>>> =
+ Arc::new(Mutex::new(HashMap::new()));
+}
+
+// parses /proc/spl/kstat/zfs/POOL/objset-ID files
+// they have the following format:
+//
+// 0 0 0x00 0 0000 00000000000 000000000000000000
+// name type data
+// dataset_name 7 pool/dataset
+// writes 4 0
+// nwritten 4 0
+// reads 4 0
+// nread 4 0
+// nunlinks 4 0
+// nunlinked 4 0
+//
+// we are only interested in the dataset_name, writes, nwrites, reads and nread
+fn parse_objset_stat(pool: &str, objset_id: &str) -> Result<(String, BlockDevStat), Error> {
+ let path = PathBuf::from(format!("{}/{}/{}", ZFS_KSTAT_BASE_PATH, pool, objset_id));
+
+ let text = match proxmox_sys::fs::file_read_optional_string(&path)? {
+ Some(text) => text,
+ None => bail!("could not parse 'objset-{}' stat file", objset_id),
+ };
+
+ let mut dataset_name = String::new();
+ let mut stat = BlockDevStat {
+ read_sectors: 0,
+ write_sectors: 0,
+ read_ios: 0,
+ write_ios: 0,
+ io_ticks: 0,
+ };
+
+ for (i, line) in text.lines().enumerate() {
+ if i < 2 {
+ continue;
+ }
+
+ let mut parts = line.split_ascii_whitespace();
+ let name = parts.next();
+ parts.next(); // discard type
+ let value = parts.next().ok_or_else(|| format_err!("no value found"))?;
+ match name {
+ Some("dataset_name") => dataset_name = value.to_string(),
+ Some("writes") => stat.write_ios = u64::from_str_radix(value, 10).unwrap_or(0),
+ Some("nwritten") => {
+ stat.write_sectors = u64::from_str_radix(value, 10).unwrap_or(0) / 512
+ }
+ Some("reads") => stat.read_ios = u64::from_str_radix(value, 10).unwrap_or(0),
+ Some("nread") => stat.read_sectors = u64::from_str_radix(value, 10).unwrap_or(0) / 512,
+ _ => {}
+ }
+ }
+
+ Ok((dataset_name, stat))
+}
+
+
+fn get_mapping(dataset: &str) -> Option<(String, String)> {
+ ZFS_DATASET_OBJSET_MAP
+ .lock()
+ .unwrap()
+ .get(dataset)
+ .map(|c| c.to_owned())
+}
+
+/// Updates the dataset <-> objset_map
+pub(crate) fn update_zfs_objset_map(pool: &str) -> Result<(), Error> {
+ let mut map = ZFS_DATASET_OBJSET_MAP.lock().unwrap();
+ map.clear();
+ let path = PathBuf::from(format!("{}/{}", ZFS_KSTAT_BASE_PATH, pool));
+
+ proxmox_sys::fs::scandir(
+ libc::AT_FDCWD,
+ &path,
+ &OBJSET_REGEX,
+ |_l2_fd, filename, _type| {
+ let (name, _) = parse_objset_stat(pool, filename)?;
+ map.insert(name, (pool.to_string(), filename.to_string()));
+ Ok(())
+ },
+ )?;
+
+ Ok(())
+}
+
+/// Gets io stats for the dataset from /proc/spl/kstat/zfs/POOL/objset-ID
+pub(crate) fn zfs_dataset_stats(dataset: &str) -> Result<BlockDevStat, Error> {
+ let mut mapping = get_mapping(dataset);
+ if mapping.is_none() {
+ let pool = get_pool_from_dataset(dataset);
+ update_zfs_objset_map(pool)?;
+ mapping = get_mapping(dataset);
+ }
+ let (pool, objset_id) =
+ mapping.ok_or_else(|| format_err!("could not find objset id for dataset"))?;
+ let (_, stat) = parse_objset_stat(&pool, &objset_id)?;
+ Ok(stat)
+}
--
2.30.2
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2021-12-15 15:16 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-12-15 15:16 [pbs-devel] [PATCH proxmox-backup] fix #3743: extract zfs dataset io stats from /proc/spl/kstat/zfs/POOL/objset-* Dominik Csapak
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal