zfs_arc: support for zfsonlinux
[collectd.git] / src / zfs_arc.c
1 /**
2  * collectd - src/zfs_arc.c
3  * Copyright (C) 2009  Anthony Dewhurst
4  * Copyright (C) 2012  Aurelien Rougemont
5  * Copyright (C) 2013  Xin Li
6  * Copyright (C) 2014  Marc Fournier
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU General Public License as published by the
10  * Free Software Foundation; only version 2 of the License is applicable.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
20  *
21  * Authors:
22  *   Anthony Dewhurst <dewhurst at gmail>
23  *   Aurelien Rougemont <beorn at gandi.net>
24  *   Xin Li <delphij at FreeBSD.org>
25  *   Marc Fournier <marc.fournier at camptocamp.com>
26  **/
27
28 #include "collectd.h"
29 #include "common.h"
30 #include "plugin.h"
31
32 /*
33  * Global variables
34  */
35
36 #if defined(KERNEL_LINUX)
37 #include "utils_llist.h"
38 #define ZOL_ARCSTATS_FILE "/proc/spl/kstat/zfs/arcstats"
39
40 typedef llist_t kstat_t;
41
42 static long long get_zfs_value(kstat_t *zfs_stats  __attribute__((unused)),
43                 char *name)
44 {
45         llentry_t *e;
46
47         e = llist_search (zfs_stats, name);
48         if (e == NULL)
49         {
50                 ERROR ("zfs_arc plugin: `llist_search` failed for key: '%s'.", name);
51                 return (-1);
52         }
53
54         return ((long long int)e->value);
55 }
56
57 #elif !defined(__FreeBSD__) // Solaris
58 extern kstat_ctl_t *kc;
59
60 static long long get_zfs_value(kstat_t *ksp, char *name)
61 {
62
63         return (get_kstat_value(ksp, name));
64 }
65 #else // FreeBSD
66 #include <sys/types.h>
67 #include <sys/sysctl.h>
68
69 const char zfs_arcstat[] = "kstat.zfs.misc.arcstats.";
70
71 #if !defined(kstat_t)
72 typedef void kstat_t;
73 #endif
74
75 static long long get_zfs_value(kstat_t *dummy __attribute__((unused)),
76                 char const *name)
77 {
78         char buffer[256];
79         long long value;
80         size_t valuelen = sizeof(value);
81         int rv;
82
83         ssnprintf (buffer, sizeof (buffer), "%s%s", zfs_arcstat, name);
84         rv = sysctlbyname (buffer, (void *) &value, &valuelen,
85                         /* new value = */ NULL, /* new length = */ (size_t) 0);
86         if (rv == 0)
87                 return (value);
88
89         return (-1);
90 }
91 #endif
92
93 static void za_submit (const char* type, const char* type_instance, value_t* values, int values_len)
94 {
95         value_list_t vl = VALUE_LIST_INIT;
96
97         vl.values = values;
98         vl.values_len = values_len;
99
100         sstrncpy (vl.host, hostname_g, sizeof (vl.host));
101         sstrncpy (vl.plugin, "zfs_arc", sizeof (vl.plugin));
102         sstrncpy (vl.type, type, sizeof (vl.type));
103         sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
104
105         plugin_dispatch_values (&vl);
106 }
107
108 static void za_submit_gauge (const char* type, const char* type_instance, gauge_t value)
109 {
110         value_t vv;
111
112         vv.gauge = value;
113         za_submit (type, type_instance, &vv, 1);
114 }
115
116 static int za_read_derive (kstat_t *ksp, const char *kstat_value,
117     const char *type, const char *type_instance)
118 {
119   long long tmp;
120   value_t v;
121
122   tmp = get_zfs_value (ksp, (char *)kstat_value);
123   if (tmp == -1LL)
124   {
125     WARNING ("zfs_arc plugin: Reading kstat value \"%s\" failed.", kstat_value);
126     return (-1);
127   }
128
129   v.derive = (derive_t) tmp;
130   za_submit (type, type_instance, /* values = */ &v, /* values_num = */ 1);
131   return (0);
132 }
133
134 static int za_read_gauge (kstat_t *ksp, const char *kstat_value,
135     const char *type, const char *type_instance)
136 {
137   long long tmp;
138   value_t v;
139
140   tmp = get_zfs_value (ksp, (char *)kstat_value);
141   if (tmp == -1LL)
142   {
143     WARNING ("zfs_arc plugin: Reading kstat value \"%s\" failed.", kstat_value);
144     return (-1);
145   }
146
147   v.gauge = (gauge_t) tmp;
148   za_submit (type, type_instance, /* values = */ &v, /* values_num = */ 1);
149   return (0);
150 }
151
152 static void za_submit_ratio (const char* type_instance, gauge_t hits, gauge_t misses)
153 {
154         gauge_t ratio = NAN;
155
156         if (!isfinite (hits) || (hits < 0.0))
157                 hits = 0.0;
158         if (!isfinite (misses) || (misses < 0.0))
159                 misses = 0.0;
160
161         if ((hits != 0.0) || (misses != 0.0))
162                 ratio = hits / (hits + misses);
163
164         za_submit_gauge ("cache_ratio", type_instance, ratio);
165 }
166
167 static int za_read (void)
168 {
169         gauge_t  arc_hits, arc_misses, l2_hits, l2_misses;
170         value_t  l2_io[2];
171         kstat_t  *ksp   = NULL;
172
173 #if KERNEL_LINUX
174         long long int *llvalues = NULL;
175         char FileContents[1024 * 10];
176         char *fields[3];
177         int numfields;
178         ssize_t len;
179
180         ksp = llist_create ();
181         if (ksp == NULL)
182         {
183                 ERROR ("zfs_arc plugin: `llist_create' failed.");
184                 return (-1);
185         }
186
187         len = read_file_contents (ZOL_ARCSTATS_FILE, FileContents, sizeof(FileContents));
188         if (len > 1)
189         {
190
191                 int i=0;
192                 char *pnl = FileContents;
193                 char *pnnl;
194
195                 FileContents[len] = '\0';
196
197                 while (pnl != NULL)
198                 {
199                         pnl = strchr(pnl, '\n');
200                         i++;
201                         if (pnl && (*pnl != '\0'))
202                                 pnl++;
203                 }
204
205                 if (i > 0)
206                 {
207                         llentry_t *e;
208                         llvalues = malloc(sizeof(long long int) * i);
209                         i = 0;
210
211                         pnl = FileContents;
212                         while (pnl != NULL)
213                         {
214                                 pnnl = strchr(pnl, '\n');
215                                 if (pnnl != NULL)
216                                         *pnnl = '\0';
217                                 
218                                 numfields = strsplit (pnl, fields, 4);
219                                 if (numfields == 3)
220                                 {
221                                         llvalues[i] = atoll (fields[2]);
222
223                                         e = llentry_create (fields[0], &llvalues[i]);
224                                         if (e == NULL)
225                                         {
226                                                 ERROR ("zfs_arc plugin: `llentry_create' failed.");
227                                         }
228                                         else
229                                         {
230                                                 llist_append (ksp, e);
231                                         }
232                                 }
233                                 pnl = pnnl;
234                                 if (pnl != NULL)
235                                         pnl ++;
236                         }
237                 }
238         }
239
240 #elif !defined(__FreeBSD__) // Solaris
241         get_kstat (&ksp, "zfs", 0, "arcstats");
242         if (ksp == NULL)
243         {
244                 ERROR ("zfs_arc plugin: Cannot find zfs:0:arcstats kstat.");
245                 return (-1);
246         }
247 #endif
248
249         /* Sizes */
250         za_read_gauge (ksp, "size",    "cache_size", "arc");
251         za_read_gauge (ksp, "l2_size", "cache_size", "L2");
252
253         /* Operations */
254         za_read_derive (ksp, "deleted",  "cache_operation", "deleted");
255 #if __FreeBSD__
256         za_read_derive (ksp, "allocated","cache_operation", "allocated");
257         za_read_derive (ksp, "stolen",   "cache_operation", "stolen");
258 #endif
259
260         /* Issue indicators */
261         za_read_derive (ksp, "mutex_miss", "mutex_operations", "miss");
262         za_read_derive (ksp, "hash_collisions", "hash_collisions", "");
263
264         /* Evictions */
265         za_read_derive (ksp, "evict_l2_cached",     "cache_eviction", "cached");
266         za_read_derive (ksp, "evict_l2_eligible",   "cache_eviction", "eligible");
267         za_read_derive (ksp, "evict_l2_ineligible", "cache_eviction", "ineligible");
268
269         /* Hits / misses */
270         za_read_derive (ksp, "demand_data_hits",         "cache_result", "demand_data-hit");
271         za_read_derive (ksp, "demand_metadata_hits",     "cache_result", "demand_metadata-hit");
272         za_read_derive (ksp, "prefetch_data_hits",       "cache_result", "prefetch_data-hit");
273         za_read_derive (ksp, "prefetch_metadata_hits",   "cache_result", "prefetch_metadata-hit");
274         za_read_derive (ksp, "demand_data_misses",       "cache_result", "demand_data-miss");
275         za_read_derive (ksp, "demand_metadata_misses",   "cache_result", "demand_metadata-miss");
276         za_read_derive (ksp, "prefetch_data_misses",     "cache_result", "prefetch_data-miss");
277         za_read_derive (ksp, "prefetch_metadata_misses", "cache_result", "prefetch_metadata-miss");
278
279         /* Ratios */
280         arc_hits   = (gauge_t) get_zfs_value(ksp, "hits");
281         arc_misses = (gauge_t) get_zfs_value(ksp, "misses");
282         l2_hits    = (gauge_t) get_zfs_value(ksp, "l2_hits");
283         l2_misses  = (gauge_t) get_zfs_value(ksp, "l2_misses");
284
285         za_submit_ratio ("arc", arc_hits, arc_misses);
286         za_submit_ratio ("L2", l2_hits, l2_misses);
287
288         /* I/O */
289         l2_io[0].derive = get_zfs_value(ksp, "l2_read_bytes");
290         l2_io[1].derive = get_zfs_value(ksp, "l2_write_bytes");
291
292         za_submit ("io_octets", "L2", l2_io, /* num values = */ 2);
293
294 #if defined(KERNEL_LINUX)
295         if (llvalues != NULL)
296         {
297                 free(llvalues);
298         }
299         if (ksp != NULL)
300         {
301            
302                 llist_destroy (ksp);
303         }
304 #endif
305
306         return (0);
307 } /* int za_read */
308
309 static int za_init (void) /* {{{ */
310 {
311 #if !defined(__FreeBSD__) && !defined(KERNEL_LINUX) // Solaris
312         /* kstats chain already opened by update_kstat (using *kc), verify everything went fine. */
313         if (kc == NULL)
314         {
315                 ERROR ("zfs_arc plugin: kstat chain control structure not available.");
316                 return (-1);
317         }
318 #endif
319
320         return (0);
321 } /* }}} int za_init */
322
323 void module_register (void)
324 {
325         plugin_register_init ("zfs_arc", za_init);
326         plugin_register_read ("zfs_arc", za_read);
327 } /* void module_register */
328
329 /* vmi: set sw=8 noexpandtab fdm=marker : */