Fix ZFS bugs (diskspace.plugin) (#22188)
thiagoftsm committed
Apr 13, 2026 at 02:08 UTC
d4e069f7298014600c1131ef0fc137a935cb5e29
1 file changed
+40
-66
src/collectors/diskspace.plugin/plugin_diskspace.c
+40
-66
@@ -162,7 +162,7 @@ static struct basic_mountinfo *basic_mountinfo_create_and_copy(struct mountinfo*
162
bmi->mount_point_stat_path = strdupz(mi->mount_point_stat_path);
163
bmi->mount_point = strdupz(mi->mount_point);
164
bmi->mount_source = mi->mount_source ? strdupz(mi->mount_source) : NULL;
165
- bmi->filesystem = strdupz(mi->filesystem);
165
+ bmi->filesystem = mi->filesystem ? strdupz(mi->filesystem) : NULL;
166
}
167
168
return bmi;
@@ -351,76 +351,44 @@ static const char *extract_zfs_pool_name(const char *mount_source, char *buf, si
351
return buf;
352
}
353
354
-static inline bool is_zfs_filesystem(struct mountinfo *mi) {
355
- return mi && mi->filesystem && strcmp(mi->filesystem, "zfs") == 0;
356
-}
357
-
358
-// Check if a ZFS filesystem entry is a dataset (not a pool)
359
-// Dataset = has '/' in mount_source (e.g., "tank/home")
360
-// Pool = no '/' in mount_source (e.g., "tank")
361
-static inline bool is_zfs_dataset(struct mountinfo *mi) {
362
- return is_zfs_filesystem(mi) &&
363
- mi->mount_source &&
364
- mi->mount_source[0] &&
365
- strchr(mi->mount_source, '/') != NULL;
366
-}
367
-
368
-// Cached LXC detection result (checked once at first call, can't change at runtime)
354
+// LXC detection result – set once at startup in diskspace_main, never changes at runtime
355
static bool zfs_inside_lxc_container = false;
356
371
-// Collect ZFS pool capacities for the heuristic
372
-// Called on every collection cycle but only updates pools (quick operation)
373
-static void zfs_collect_pool_capacities(void) {
374
- // LXC detection - only once (can't change at runtime)
375
- static bool lxc_checked = false;
376
- if (!lxc_checked) {
377
- zfs_inside_lxc_container = is_lxcfs_proc_mounted();
378
- lxc_checked = true;
379
- }
380
-
357
+// Cache the capacity of a ZFS pool mount into zfs_cache.
358
+// Called from do_disk_space_stats / do_slow_disk_space_stats after their existing statvfs()
359
+// succeeds, so no extra blocking call is introduced.
360
+static void zfs_cache_pool_capacity(const char *filesystem, const char *mount_source,
361
+ const struct statvfs *buff)
362
+{
363
if (!zfs_datasets_heuristic || !zfs_cache)
364
return;
365
+ if (!filesystem || strcmp(filesystem, "zfs") != 0)
366
+ return;
367
+ // Only pool mounts – datasets have '/' in mount_source
368
+ if (!mount_source || !mount_source[0] || strchr(mount_source, '/'))
369
+ return;
370
371
+ // Skip if the cached entry is still fresh
372
time_t now = now_realtime_sec();
373
+ const DICTIONARY_ITEM *existing = dictionary_get_and_acquire_item(zfs_cache, mount_source);
374
+ if (existing) {
375
+ struct zfs_cache_entry *entry = dictionary_acquired_item_value(existing);
376
+ bool fresh = entry->is_pool && (now - entry->last_checked < ZFS_DATASET_RECHECK_SECONDS);
377
+ dictionary_acquired_item_release(zfs_cache, existing);
378
+ if (fresh)
379
+ return;
380
+ }
381
386
- for (struct mountinfo *mi = disk_mountinfo_root; mi; mi = mi->next) {
387
- if (!is_zfs_filesystem(mi))
388
- continue;
389
-
390
- if (!mi->mount_source || !mi->mount_source[0])
391
- continue;
392
-
393
- // Only process pool mounts (no '/' in mount_source), not datasets
394
- if (strchr(mi->mount_source, '/'))
395
- continue;
396
-
397
- // Check if this pool entry needs refresh
398
- const DICTIONARY_ITEM *existing = dictionary_get_and_acquire_item(zfs_cache, mi->mount_source);
399
- if (existing) {
400
- struct zfs_cache_entry *entry = dictionary_acquired_item_value(existing);
401
- if (entry->is_pool && now - entry->last_checked < ZFS_DATASET_RECHECK_SECONDS) {
402
- dictionary_acquired_item_release(zfs_cache, existing);
403
- continue; // still fresh
404
- }
405
- dictionary_acquired_item_release(zfs_cache, existing);
406
- }
407
-
408
- // Get capacity from the pool mount
409
- struct statvfs buff;
410
- if (statvfs(mi->mount_point_stat_path, &buff) != 0)
411
- continue;
412
-
413
- unsigned long bsize = buff.f_frsize ? buff.f_frsize : buff.f_bsize;
414
- uint64_t capacity = (uint64_t)buff.f_blocks * bsize;
382
+ unsigned long bsize = buff->f_frsize ? buff->f_frsize : buff->f_bsize;
383
+ uint64_t capacity = (uint64_t)buff->f_blocks * bsize;
384
416
- struct zfs_cache_entry new_entry = {
417
- .last_checked = now,
418
- .is_pool = true,
419
- .pool_capacity = capacity,
420
- .excluded = false
421
- };
422
- dictionary_set(zfs_cache, mi->mount_source, &new_entry, sizeof(new_entry));
423
- }
385
+ struct zfs_cache_entry new_entry = {
386
+ .last_checked = now,
387
+ .is_pool = true,
388
+ .pool_capacity = capacity,
389
+ .excluded = false
390
+ };
391
+ dictionary_set(zfs_cache, mount_source, &new_entry, sizeof(new_entry));
392
}
393
394
// Check if a ZFS dataset has a cached exclusion decision that's still valid
@@ -720,6 +688,9 @@ static inline void do_disk_space_stats(struct mountinfo *mi, int update_every) {
688
if ((now_monotonic_high_precision_usec() - start_time) > slow_timeout)
689
m->slow = true;
690
691
+ // Cache ZFS pool capacity so dataset exclusion heuristic can work next cycle
692
+ zfs_cache_pool_capacity(mi->filesystem, mi->mount_source, &buff_statvfs);
693
+
694
// Check if this ZFS mount should be excluded (capacity-based heuristic)
695
if (should_exclude_zfs(mi->filesystem, mi->mount_point, mi->mount_source, &buff_statvfs))
696
goto cleanup;
@@ -760,6 +731,9 @@ static inline void do_slow_disk_space_stats(struct basic_mountinfo *mi, int upda
731
}
732
m->shown_error = false;
733
734
+ // Cache ZFS pool capacity so dataset exclusion heuristic can work next cycle
735
+ zfs_cache_pool_capacity(mi->filesystem, mi->mount_source, &buff_statvfs);
736
+
737
// Check if this ZFS mount should be excluded (same logic as fast path)
738
if (should_exclude_zfs(mi->filesystem, mi->mount_point, mi->mount_source, &buff_statvfs))
739
goto cleanup;
@@ -1151,6 +1125,9 @@ void diskspace_main(void *ptr) {
1125
diskspace_slow_worker,
1126
&slow_worker_data);
1127
1128
+ // LXC detection – done once; virtualised mounts inside LXC bypass the ZFS exclusion heuristic
1129
+ zfs_inside_lxc_container = is_lxcfs_proc_mounted();
1130
+
1131
heartbeat_t hb;
1132
heartbeat_init(&hb, update_every * USEC_PER_SEC);
1133
while(service_running(SERVICE_COLLECTORS)) {
@@ -1172,9 +1149,6 @@ void diskspace_main(void *ptr) {
1149
free_basic_mountinfo_list(slow_mountinfo_tmp_root);
1150
slow_mountinfo_tmp_root = NULL;
1151
1175
- // Collect ZFS pool capacities for the heuristic (Pass 1)
1176
- zfs_collect_pool_capacities();
1177
-
1152
struct mountinfo *mi;
1153
for(mi = disk_mountinfo_root; mi; mi = mi->next) {
1154
if(unlikely(mi->flags & (MOUNTINFO_IS_DUMMY | MOUNTINFO_IS_BIND)))