| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #include "plugin_proc.h" |
| 4 | #include "zfs_common.h" |
| 5 | |
| 6 | #define ZFS_PROC_ARCSTATS "/proc/spl/kstat/zfs/arcstats" |
| 7 | #define ZFS_PROC_POOLS "/proc/spl/kstat/zfs" |
| 8 | |
| 9 | #define STATE_SIZE 20 |
| 10 | #define MAX_CHART_ID 256 |
| 11 | |
| 12 | extern struct arcstats arcstats; |
| 13 | |
| 14 | unsigned long long zfs_arcstats_shrinkable_cache_size_bytes = 0; |
| 15 | |
| 16 | int do_proc_spl_kstat_zfs_arcstats(int update_every, usec_t dt) { |
| 17 | (void)dt; |
| 18 | |
| 19 | static int do_zfs_stats = 0; |
| 20 | static procfile *ff = NULL; |
| 21 | static const char *dirname = NULL; |
| 22 | static ARL_BASE *arl_base = NULL; |
| 23 | |
| 24 | arcstats.l2exist = -1; |
| 25 | |
| 26 | if(unlikely(!arl_base)) { |
| 27 | arl_base = arl_create("arcstats", NULL, 60); |
| 28 | |
| 29 | arl_expect(arl_base, "hits", &arcstats.hits); |
| 30 | arl_expect(arl_base, "misses", &arcstats.misses); |
| 31 | arl_expect(arl_base, "demand_data_hits", &arcstats.demand_data_hits); |
| 32 | arl_expect(arl_base, "demand_data_misses", &arcstats.demand_data_misses); |
| 33 | arl_expect(arl_base, "demand_metadata_hits", &arcstats.demand_metadata_hits); |
| 34 | arl_expect(arl_base, "demand_metadata_misses", &arcstats.demand_metadata_misses); |
| 35 | arl_expect(arl_base, "prefetch_data_hits", &arcstats.prefetch_data_hits); |
| 36 | arl_expect(arl_base, "prefetch_data_misses", &arcstats.prefetch_data_misses); |
| 37 | arl_expect(arl_base, "prefetch_metadata_hits", &arcstats.prefetch_metadata_hits); |
| 38 | arl_expect(arl_base, "prefetch_metadata_misses", &arcstats.prefetch_metadata_misses); |
| 39 | arl_expect(arl_base, "mru_hits", &arcstats.mru_hits); |
| 40 | arl_expect(arl_base, "mru_ghost_hits", &arcstats.mru_ghost_hits); |
| 41 | arl_expect(arl_base, "mfu_hits", &arcstats.mfu_hits); |
| 42 | arl_expect(arl_base, "mfu_ghost_hits", &arcstats.mfu_ghost_hits); |
| 43 | arl_expect(arl_base, "deleted", &arcstats.deleted); |
| 44 | arl_expect(arl_base, "mutex_miss", &arcstats.mutex_miss); |
| 45 | arl_expect(arl_base, "evict_skip", &arcstats.evict_skip); |
| 46 | arl_expect(arl_base, "evict_not_enough", &arcstats.evict_not_enough); |
| 47 | arl_expect(arl_base, "evict_l2_cached", &arcstats.evict_l2_cached); |
| 48 | arl_expect(arl_base, "evict_l2_eligible", &arcstats.evict_l2_eligible); |
| 49 | arl_expect(arl_base, "evict_l2_ineligible", &arcstats.evict_l2_ineligible); |
| 50 | arl_expect(arl_base, "evict_l2_skip", &arcstats.evict_l2_skip); |
| 51 | arl_expect(arl_base, "hash_elements", &arcstats.hash_elements); |
| 52 | arl_expect(arl_base, "hash_elements_max", &arcstats.hash_elements_max); |
| 53 | arl_expect(arl_base, "hash_collisions", &arcstats.hash_collisions); |
| 54 | arl_expect(arl_base, "hash_chains", &arcstats.hash_chains); |
| 55 | arl_expect(arl_base, "hash_chain_max", &arcstats.hash_chain_max); |
| 56 | arl_expect(arl_base, "p", &arcstats.p); |
| 57 | arl_expect(arl_base, "pd", &arcstats.pd); |
| 58 | arl_expect(arl_base, "pm", &arcstats.pm); |
| 59 | arl_expect(arl_base, "c", &arcstats.c); |
| 60 | arl_expect(arl_base, "c_min", &arcstats.c_min); |
| 61 | arl_expect(arl_base, "c_max", &arcstats.c_max); |
| 62 | arl_expect(arl_base, "size", &arcstats.size); |
| 63 | arl_expect(arl_base, "hdr_size", &arcstats.hdr_size); |
| 64 | arl_expect(arl_base, "data_size", &arcstats.data_size); |
| 65 | arl_expect(arl_base, "metadata_size", &arcstats.metadata_size); |
| 66 | arl_expect(arl_base, "other_size", &arcstats.other_size); |
| 67 | arl_expect(arl_base, "anon_size", &arcstats.anon_size); |
| 68 | arl_expect(arl_base, "anon_evictable_data", &arcstats.anon_evictable_data); |
| 69 | arl_expect(arl_base, "anon_evictable_metadata", &arcstats.anon_evictable_metadata); |
| 70 | arl_expect(arl_base, "mru_size", &arcstats.mru_size); |
| 71 | arl_expect(arl_base, "mru_evictable_data", &arcstats.mru_evictable_data); |
| 72 | arl_expect(arl_base, "mru_evictable_metadata", &arcstats.mru_evictable_metadata); |
| 73 | arl_expect(arl_base, "mru_ghost_size", &arcstats.mru_ghost_size); |
| 74 | arl_expect(arl_base, "mru_ghost_evictable_data", &arcstats.mru_ghost_evictable_data); |
| 75 | arl_expect(arl_base, "mru_ghost_evictable_metadata", &arcstats.mru_ghost_evictable_metadata); |
| 76 | arl_expect(arl_base, "mfu_size", &arcstats.mfu_size); |
| 77 | arl_expect(arl_base, "mfu_evictable_data", &arcstats.mfu_evictable_data); |
| 78 | arl_expect(arl_base, "mfu_evictable_metadata", &arcstats.mfu_evictable_metadata); |
| 79 | arl_expect(arl_base, "mfu_ghost_size", &arcstats.mfu_ghost_size); |
| 80 | arl_expect(arl_base, "mfu_ghost_evictable_data", &arcstats.mfu_ghost_evictable_data); |
| 81 | arl_expect(arl_base, "mfu_ghost_evictable_metadata", &arcstats.mfu_ghost_evictable_metadata); |
| 82 | arl_expect(arl_base, "l2_hits", &arcstats.l2_hits); |
| 83 | arl_expect(arl_base, "l2_misses", &arcstats.l2_misses); |
| 84 | arl_expect(arl_base, "l2_feeds", &arcstats.l2_feeds); |
| 85 | arl_expect(arl_base, "l2_rw_clash", &arcstats.l2_rw_clash); |
| 86 | arl_expect(arl_base, "l2_read_bytes", &arcstats.l2_read_bytes); |
| 87 | arl_expect(arl_base, "l2_write_bytes", &arcstats.l2_write_bytes); |
| 88 | arl_expect(arl_base, "l2_writes_sent", &arcstats.l2_writes_sent); |
| 89 | arl_expect(arl_base, "l2_writes_done", &arcstats.l2_writes_done); |
| 90 | arl_expect(arl_base, "l2_writes_error", &arcstats.l2_writes_error); |
| 91 | arl_expect(arl_base, "l2_writes_lock_retry", &arcstats.l2_writes_lock_retry); |
| 92 | arl_expect(arl_base, "l2_evict_lock_retry", &arcstats.l2_evict_lock_retry); |
| 93 | arl_expect(arl_base, "l2_evict_reading", &arcstats.l2_evict_reading); |
| 94 | arl_expect(arl_base, "l2_evict_l1cached", &arcstats.l2_evict_l1cached); |
| 95 | arl_expect(arl_base, "l2_free_on_write", &arcstats.l2_free_on_write); |
| 96 | arl_expect(arl_base, "l2_cdata_free_on_write", &arcstats.l2_cdata_free_on_write); |
| 97 | arl_expect(arl_base, "l2_abort_lowmem", &arcstats.l2_abort_lowmem); |
| 98 | arl_expect(arl_base, "l2_cksum_bad", &arcstats.l2_cksum_bad); |
| 99 | arl_expect(arl_base, "l2_io_error", &arcstats.l2_io_error); |
| 100 | arl_expect(arl_base, "l2_size", &arcstats.l2_size); |
| 101 | arl_expect(arl_base, "l2_asize", &arcstats.l2_asize); |
| 102 | arl_expect(arl_base, "l2_hdr_size", &arcstats.l2_hdr_size); |
| 103 | arl_expect(arl_base, "l2_compress_successes", &arcstats.l2_compress_successes); |
| 104 | arl_expect(arl_base, "l2_compress_zeros", &arcstats.l2_compress_zeros); |
| 105 | arl_expect(arl_base, "l2_compress_failures", &arcstats.l2_compress_failures); |
| 106 | arl_expect(arl_base, "memory_throttle_count", &arcstats.memory_throttle_count); |
| 107 | arl_expect(arl_base, "duplicate_buffers", &arcstats.duplicate_buffers); |
| 108 | arl_expect(arl_base, "duplicate_buffers_size", &arcstats.duplicate_buffers_size); |
| 109 | arl_expect(arl_base, "duplicate_reads", &arcstats.duplicate_reads); |
| 110 | arl_expect(arl_base, "memory_direct_count", &arcstats.memory_direct_count); |
| 111 | arl_expect(arl_base, "memory_indirect_count", &arcstats.memory_indirect_count); |
| 112 | arl_expect(arl_base, "arc_no_grow", &arcstats.arc_no_grow); |
| 113 | arl_expect(arl_base, "arc_tempreserve", &arcstats.arc_tempreserve); |
| 114 | arl_expect(arl_base, "arc_loaned_bytes", &arcstats.arc_loaned_bytes); |
| 115 | arl_expect(arl_base, "arc_prune", &arcstats.arc_prune); |
| 116 | arl_expect(arl_base, "arc_meta_used", &arcstats.arc_meta_used); |
| 117 | arl_expect(arl_base, "arc_meta_limit", &arcstats.arc_meta_limit); |
| 118 | arl_expect(arl_base, "arc_meta_max", &arcstats.arc_meta_max); |
| 119 | arl_expect(arl_base, "arc_meta_min", &arcstats.arc_meta_min); |
| 120 | arl_expect(arl_base, "arc_need_free", &arcstats.arc_need_free); |
| 121 | arl_expect(arl_base, "arc_sys_free", &arcstats.arc_sys_free); |
| 122 | } |
| 123 | |
| 124 | if(unlikely(!ff)) { |
| 125 | char filename[FILENAME_MAX + 1]; |
| 126 | snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, ZFS_PROC_ARCSTATS); |
| 127 | ff = procfile_open(inicfg_get(&netdata_config, "plugin:proc:" ZFS_PROC_ARCSTATS, "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT); |
| 128 | if(unlikely(!ff)) |
| 129 | return 1; |
| 130 | |
| 131 | snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/spl/kstat/zfs"); |
| 132 | dirname = inicfg_get(&netdata_config, "plugin:proc:" ZFS_PROC_ARCSTATS, "directory to monitor", filename); |
| 133 | } |
| 134 | |
| 135 | // check if any pools exist |
| 136 | if(likely(!do_zfs_stats)) { |
| 137 | DIR *dir = opendir(dirname); |
| 138 | if(unlikely(!dir)) { |
| 139 | collector_error("Cannot read directory '%s'", dirname); |
| 140 | return 1; |
| 141 | } |
| 142 | |
| 143 | struct dirent *de = NULL; |
| 144 | while(likely(de = readdir(dir))) { |
| 145 | if(likely(de->d_type == DT_DIR |
| 146 | && ( |
| 147 | (de->d_name[0] == '.' && de->d_name[1] == '\0') |
| 148 | || (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0') |
| 149 | ))) |
| 150 | continue; |
| 151 | |
| 152 | if(unlikely(de->d_type == DT_LNK || de->d_type == DT_DIR)) { |
| 153 | do_zfs_stats = 1; |
| 154 | break; |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | closedir(dir); |
| 159 | } |
| 160 | |
| 161 | // do not show ZFS filesystem metrics if there haven't been any pools in the system yet |
| 162 | if(unlikely(!do_zfs_stats)) |
| 163 | return 0; |
| 164 | |
| 165 | ff = procfile_readall(ff); |
| 166 | if(unlikely(!ff)) |
| 167 | return 0; // we return 0, so that we will retry to open it next time |
| 168 | |
| 169 | size_t lines = procfile_lines(ff), l; |
| 170 | |
| 171 | arl_begin(arl_base); |
| 172 | |
| 173 | for(l = 0; l < lines ;l++) { |
| 174 | size_t words = procfile_linewords(ff, l); |
| 175 | if(unlikely(words < 3)) { |
| 176 | if(unlikely(words)) collector_error("Cannot read " ZFS_PROC_ARCSTATS " line %zu. Expected 3 params, read %zu.", l, words); |
| 177 | continue; |
| 178 | } |
| 179 | |
| 180 | const char *key = procfile_lineword(ff, l, 0); |
| 181 | const char *value = procfile_lineword(ff, l, 2); |
| 182 | |
| 183 | if(unlikely(arcstats.l2exist == -1)) { |
| 184 | if(key[0] == 'l' && key[1] == '2' && key[2] == '_') |
| 185 | arcstats.l2exist = 1; |
| 186 | } |
| 187 | |
| 188 | if(unlikely(arl_check(arl_base, key, value))) break; |
| 189 | } |
| 190 | |
| 191 | if (arcstats.size > arcstats.c_min) { |
| 192 | zfs_arcstats_shrinkable_cache_size_bytes = arcstats.size - arcstats.c_min; |
| 193 | } else { |
| 194 | zfs_arcstats_shrinkable_cache_size_bytes = 0; |
| 195 | } |
| 196 | |
| 197 | if(unlikely(arcstats.l2exist == -1)) |
| 198 | arcstats.l2exist = 0; |
| 199 | |
| 200 | generate_charts_arcstats(PLUGIN_PROC_NAME, ZFS_PROC_ARCSTATS, update_every); |
| 201 | generate_charts_arc_summary(PLUGIN_PROC_NAME, ZFS_PROC_ARCSTATS, update_every); |
| 202 | |
| 203 | return 0; |
| 204 | } |