1*9272c79bSmlelstv /* $NetBSD: subr_iostat.c,v 1.26 2024/05/04 13:33:18 mlelstv Exp $ */
2eb5227b8Sblymn /* NetBSD: subr_disk.c,v 1.69 2005/05/29 22:24:15 christos Exp */
3eb5227b8Sblymn
4eb5227b8Sblymn /*-
54307e10dSad * Copyright (c) 1996, 1997, 1999, 2000, 2009 The NetBSD Foundation, Inc.
6eb5227b8Sblymn * All rights reserved.
7eb5227b8Sblymn *
8eb5227b8Sblymn * This code is derived from software contributed to The NetBSD Foundation
9eb5227b8Sblymn * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
10eb5227b8Sblymn * NASA Ames Research Center.
11eb5227b8Sblymn *
12eb5227b8Sblymn * Redistribution and use in source and binary forms, with or without
13eb5227b8Sblymn * modification, are permitted provided that the following conditions
14eb5227b8Sblymn * are met:
15eb5227b8Sblymn * 1. Redistributions of source code must retain the above copyright
16eb5227b8Sblymn * notice, this list of conditions and the following disclaimer.
17eb5227b8Sblymn * 2. Redistributions in binary form must reproduce the above copyright
18eb5227b8Sblymn * notice, this list of conditions and the following disclaimer in the
19eb5227b8Sblymn * documentation and/or other materials provided with the distribution.
20eb5227b8Sblymn *
21eb5227b8Sblymn * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
22eb5227b8Sblymn * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23eb5227b8Sblymn * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24eb5227b8Sblymn * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
25eb5227b8Sblymn * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26eb5227b8Sblymn * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27eb5227b8Sblymn * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28eb5227b8Sblymn * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29eb5227b8Sblymn * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30eb5227b8Sblymn * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31eb5227b8Sblymn * POSSIBILITY OF SUCH DAMAGE.
32eb5227b8Sblymn */
33eb5227b8Sblymn
34eb5227b8Sblymn /*
35eb5227b8Sblymn * Copyright (c) 1982, 1986, 1988, 1993
36eb5227b8Sblymn * The Regents of the University of California. All rights reserved.
37eb5227b8Sblymn * (c) UNIX System Laboratories, Inc.
38eb5227b8Sblymn * All or some portions of this file are derived from material licensed
39eb5227b8Sblymn * to the University of California by American Telephone and Telegraph
40eb5227b8Sblymn * Co. or Unix System Laboratories, Inc. and are reproduced herein with
41eb5227b8Sblymn * the permission of UNIX System Laboratories, Inc.
42eb5227b8Sblymn *
43eb5227b8Sblymn * Redistribution and use in source and binary forms, with or without
44eb5227b8Sblymn * modification, are permitted provided that the following conditions
45eb5227b8Sblymn * are met:
46eb5227b8Sblymn * 1. Redistributions of source code must retain the above copyright
47eb5227b8Sblymn * notice, this list of conditions and the following disclaimer.
48eb5227b8Sblymn * 2. Redistributions in binary form must reproduce the above copyright
49eb5227b8Sblymn * notice, this list of conditions and the following disclaimer in the
50eb5227b8Sblymn * documentation and/or other materials provided with the distribution.
51eb5227b8Sblymn * 3. Neither the name of the University nor the names of its contributors
52eb5227b8Sblymn * may be used to endorse or promote products derived from this software
53eb5227b8Sblymn * without specific prior written permission.
54eb5227b8Sblymn *
55eb5227b8Sblymn * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
56eb5227b8Sblymn * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
57eb5227b8Sblymn * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
58eb5227b8Sblymn * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
59eb5227b8Sblymn * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
60eb5227b8Sblymn * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
61eb5227b8Sblymn * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62eb5227b8Sblymn * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
63eb5227b8Sblymn * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
64eb5227b8Sblymn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
65eb5227b8Sblymn * SUCH DAMAGE.
66eb5227b8Sblymn *
67eb5227b8Sblymn * @(#)ufs_disksubr.c 8.5 (Berkeley) 1/21/94
68eb5227b8Sblymn */
69eb5227b8Sblymn
70eb5227b8Sblymn #include <sys/cdefs.h>
71*9272c79bSmlelstv __KERNEL_RCSID(0, "$NetBSD: subr_iostat.c,v 1.26 2024/05/04 13:33:18 mlelstv Exp $");
72eb5227b8Sblymn
73eb5227b8Sblymn #include <sys/param.h>
74eb5227b8Sblymn #include <sys/kernel.h>
75cb1e92d5Syamt #include <sys/kmem.h>
76f7fc2f12Syamt #include <sys/iostat.h>
77eb5227b8Sblymn #include <sys/sysctl.h>
78b07ec3fcSad #include <sys/rwlock.h>
79eb5227b8Sblymn
80eb5227b8Sblymn /*
81eb5227b8Sblymn * Function prototypes for sysctl nodes
82eb5227b8Sblymn */
83eb5227b8Sblymn static int sysctl_hw_disknames(SYSCTLFN_PROTO);
84eb5227b8Sblymn static int sysctl_hw_iostatnames(SYSCTLFN_PROTO);
85eb5227b8Sblymn static int sysctl_hw_iostats(SYSCTLFN_PROTO);
86eb5227b8Sblymn
87eb5227b8Sblymn static int
88eb5227b8Sblymn iostati_getnames(int disk_only, char *oldp, size_t *oldlenp, const void *newp,
89eb5227b8Sblymn u_int namelen);
90eb5227b8Sblymn
91eb5227b8Sblymn /*
92eb5227b8Sblymn * A global list of all drives attached to the system. May grow or
93eb5227b8Sblymn * shrink over time.
94eb5227b8Sblymn */
95eb5227b8Sblymn struct iostatlist_head iostatlist = TAILQ_HEAD_INITIALIZER(iostatlist);
96eb5227b8Sblymn int iostat_count; /* number of drives in global drivelist */
97b07ec3fcSad krwlock_t iostatlist_lock;
98f4563242Sad
9911281f01Spooka static void sysctl_io_stats_setup(struct sysctllog **);
10011281f01Spooka
101f4563242Sad /*
102f4563242Sad * Initialise the iostat subsystem.
103f4563242Sad */
104f4563242Sad void
iostat_init(void)105f4563242Sad iostat_init(void)
106f4563242Sad {
107f4563242Sad
108b07ec3fcSad rw_init(&iostatlist_lock);
10911281f01Spooka sysctl_io_stats_setup(NULL);
110f4563242Sad }
111eb5227b8Sblymn
112eb5227b8Sblymn /*
113eb5227b8Sblymn * Searches the iostatlist for the iostat corresponding to the
114eb5227b8Sblymn * name provided.
115eb5227b8Sblymn */
116eb5227b8Sblymn struct io_stats *
iostat_find(const char * name)117b9eb99f9Syamt iostat_find(const char *name)
118eb5227b8Sblymn {
119eb5227b8Sblymn struct io_stats *iostatp;
120eb5227b8Sblymn
121b9eb99f9Syamt KASSERT(name != NULL);
122eb5227b8Sblymn
123b07ec3fcSad rw_enter(&iostatlist_lock, RW_READER);
124b9eb99f9Syamt TAILQ_FOREACH(iostatp, &iostatlist, io_link) {
12510df330cSblymn if (strcmp(iostatp->io_name, name) == 0) {
126b9eb99f9Syamt break;
127b9eb99f9Syamt }
128eb5227b8Sblymn }
129b07ec3fcSad rw_exit(&iostatlist_lock);
130eb5227b8Sblymn
131b9eb99f9Syamt return iostatp;
132eb5227b8Sblymn }
133eb5227b8Sblymn
134eb5227b8Sblymn /*
135eb5227b8Sblymn * Allocate and initialise memory for the i/o statistics.
136eb5227b8Sblymn */
1376237cabcSyamt struct io_stats *
iostat_alloc(int32_t type,void * parent,const char * name)138f73c6e59Schristos iostat_alloc(int32_t type, void *parent, const char *name)
139eb5227b8Sblymn {
140eb5227b8Sblymn struct io_stats *stats;
141eb5227b8Sblymn
142cb1e92d5Syamt stats = kmem_zalloc(sizeof(*stats), KM_SLEEP);
14310df330cSblymn stats->io_type = type;
144f73c6e59Schristos stats->io_parent = parent;
145f73c6e59Schristos (void)strlcpy(stats->io_name, name, sizeof(stats->io_name));
146eb5227b8Sblymn
147eb5227b8Sblymn /*
148eb5227b8Sblymn * Set the attached timestamp.
149eb5227b8Sblymn */
150de4337abSkardel getmicrouptime(&stats->io_attachtime);
151eb5227b8Sblymn
152eb5227b8Sblymn /*
153eb5227b8Sblymn * Link into the drivelist.
154eb5227b8Sblymn */
155b07ec3fcSad rw_enter(&iostatlist_lock, RW_WRITER);
15610df330cSblymn TAILQ_INSERT_TAIL(&iostatlist, stats, io_link);
157eb5227b8Sblymn iostat_count++;
158b07ec3fcSad rw_exit(&iostatlist_lock);
159eb5227b8Sblymn
160eb5227b8Sblymn return stats;
161eb5227b8Sblymn }
162eb5227b8Sblymn
163eb5227b8Sblymn /*
164eb5227b8Sblymn * Remove i/o from stats collection.
165eb5227b8Sblymn */
166eb5227b8Sblymn void
iostat_free(struct io_stats * stats)167eb5227b8Sblymn iostat_free(struct io_stats *stats)
168eb5227b8Sblymn {
169eb5227b8Sblymn
170eb5227b8Sblymn /*
171eb5227b8Sblymn * Remove from the iostat list.
172eb5227b8Sblymn */
173eb5227b8Sblymn if (iostat_count == 0)
174eb5227b8Sblymn panic("iostat_free: iostat_count == 0");
175b07ec3fcSad rw_enter(&iostatlist_lock, RW_WRITER);
17610df330cSblymn TAILQ_REMOVE(&iostatlist, stats, io_link);
177eb5227b8Sblymn iostat_count--;
178b07ec3fcSad rw_exit(&iostatlist_lock);
179cb1e92d5Syamt kmem_free(stats, sizeof(*stats));
180eb5227b8Sblymn }
181eb5227b8Sblymn
182eb5227b8Sblymn /*
183c54265e6Shannken * Rename i/o stats.
184c54265e6Shannken */
185c54265e6Shannken void
iostat_rename(struct io_stats * stats,const char * name)186c54265e6Shannken iostat_rename(struct io_stats *stats, const char *name)
187c54265e6Shannken {
188c54265e6Shannken
189c54265e6Shannken rw_enter(&iostatlist_lock, RW_WRITER);
190c54265e6Shannken (void)strlcpy(stats->io_name, name, sizeof(stats->io_name));
191c54265e6Shannken rw_exit(&iostatlist_lock);
192c54265e6Shannken }
193c54265e6Shannken
194c54265e6Shannken /*
195ba576b71Smlelstv * multiply timeval by unsigned integer and add to result
196ba576b71Smlelstv */
197ba576b71Smlelstv static void
timermac(struct timeval * a,uint64_t count,struct timeval * res)198ba576b71Smlelstv timermac(struct timeval *a, uint64_t count, struct timeval *res)
199ba576b71Smlelstv {
200ba576b71Smlelstv struct timeval part = *a;
201ba576b71Smlelstv
202ba576b71Smlelstv while (count) {
203ba576b71Smlelstv if (count & 1)
204ba576b71Smlelstv timeradd(res, &part, res);
205ba576b71Smlelstv timeradd(&part, &part, &part);
206ba576b71Smlelstv count >>= 1;
207ba576b71Smlelstv }
208ba576b71Smlelstv }
209ba576b71Smlelstv
210ba576b71Smlelstv /*
211ba576b71Smlelstv * Increment the iostat wait counter.
212ba576b71Smlelstv * Accumulate wait time and timesum.
213ba576b71Smlelstv *
214ba576b71Smlelstv * Wait time is spent in the device bufq.
215ba576b71Smlelstv */
216ba576b71Smlelstv void
iostat_wait(struct io_stats * stats)217ba576b71Smlelstv iostat_wait(struct io_stats *stats)
218ba576b71Smlelstv {
219ba576b71Smlelstv struct timeval dv_time, diff_time;
220ba576b71Smlelstv int32_t count;
221ba576b71Smlelstv
222ba576b71Smlelstv KASSERT(stats->io_wait >= 0);
223ba576b71Smlelstv
224ba576b71Smlelstv getmicrouptime(&dv_time);
225ba576b71Smlelstv
226ba576b71Smlelstv timersub(&dv_time, &stats->io_waitstamp, &diff_time);
227ba576b71Smlelstv count = stats->io_wait++;
228ba576b71Smlelstv if (count != 0) {
229ba576b71Smlelstv timermac(&diff_time, count, &stats->io_waitsum);
230ba576b71Smlelstv timeradd(&stats->io_waittime, &diff_time, &stats->io_waittime);
231ba576b71Smlelstv }
232ba576b71Smlelstv stats->io_waitstamp = dv_time;
233ba576b71Smlelstv }
234ba576b71Smlelstv
235ba576b71Smlelstv /*
236ba576b71Smlelstv * Decrement the iostat wait counter.
237ba576b71Smlelstv * Increment the iostat busy counter.
238ba576b71Smlelstv * Accumulate wait and busy times and timesums.
239ba576b71Smlelstv *
240ba576b71Smlelstv * Busy time is spent being processed by the device.
241ba576b71Smlelstv *
242ba576b71Smlelstv * Old devices do not yet measure wait time, so skip
243ba576b71Smlelstv * processing it if the counter is still zero.
244eb5227b8Sblymn */
245eb5227b8Sblymn void
iostat_busy(struct io_stats * stats)246eb5227b8Sblymn iostat_busy(struct io_stats *stats)
247eb5227b8Sblymn {
248ba576b71Smlelstv struct timeval dv_time, diff_time;
249ba576b71Smlelstv int32_t count;
250eb5227b8Sblymn
251ba576b71Smlelstv KASSERT(stats->io_wait >= 0); /* > 0 when iostat_wait is used */
252ba576b71Smlelstv KASSERT(stats->io_busy >= 0);
253ba576b71Smlelstv
254ba576b71Smlelstv getmicrouptime(&dv_time);
255ba576b71Smlelstv
256ba576b71Smlelstv timersub(&dv_time, &stats->io_waitstamp, &diff_time);
257ba576b71Smlelstv if (stats->io_wait != 0) {
258ba576b71Smlelstv count = stats->io_wait--;
259ba576b71Smlelstv timermac(&diff_time, count, &stats->io_waitsum);
260ba576b71Smlelstv timeradd(&stats->io_waittime, &diff_time, &stats->io_waittime);
261ba576b71Smlelstv }
262ba576b71Smlelstv stats->io_waitstamp = dv_time;
263ba576b71Smlelstv
264ba576b71Smlelstv timersub(&dv_time, &stats->io_busystamp, &diff_time);
265ba576b71Smlelstv count = stats->io_busy++;
266ba576b71Smlelstv if (count != 0) {
267ba576b71Smlelstv timermac(&diff_time, count, &stats->io_busysum);
268ba576b71Smlelstv timeradd(&stats->io_busytime, &diff_time, &stats->io_busytime);
269ba576b71Smlelstv }
270ba576b71Smlelstv stats->io_busystamp = dv_time;
271eb5227b8Sblymn }
272eb5227b8Sblymn
273eb5227b8Sblymn /*
274ba576b71Smlelstv * Decrement the iostat busy counter, increment the byte count.
275ba576b71Smlelstv * Accumulate busy time and timesum.
276eb5227b8Sblymn */
277eb5227b8Sblymn void
iostat_unbusy(struct io_stats * stats,long bcount,int read)278eb5227b8Sblymn iostat_unbusy(struct io_stats *stats, long bcount, int read)
279eb5227b8Sblymn {
280eb5227b8Sblymn struct timeval dv_time, diff_time;
281ba576b71Smlelstv int32_t count;
282eb5227b8Sblymn
283ba576b71Smlelstv KASSERT(stats->io_busy > 0);
284eb5227b8Sblymn
285de4337abSkardel getmicrouptime(&dv_time);
28610df330cSblymn stats->io_timestamp = dv_time;
287ba576b71Smlelstv
288ba576b71Smlelstv /* any op */
289ba576b71Smlelstv timersub(&dv_time, &stats->io_busystamp, &diff_time);
290ba576b71Smlelstv count = stats->io_busy--;
291ba576b71Smlelstv timermac(&diff_time, count, &stats->io_busysum);
292ba576b71Smlelstv timeradd(&stats->io_busytime, &diff_time, &stats->io_busytime);
293ba576b71Smlelstv stats->io_busystamp = dv_time;
294ba576b71Smlelstv
295eb5227b8Sblymn if (bcount > 0) {
296eb5227b8Sblymn if (read) {
29710df330cSblymn stats->io_rbytes += bcount;
29810df330cSblymn stats->io_rxfer++;
299eb5227b8Sblymn } else {
30010df330cSblymn stats->io_wbytes += bcount;
30110df330cSblymn stats->io_wxfer++;
302eb5227b8Sblymn }
303eb5227b8Sblymn }
304eb5227b8Sblymn }
305eb5227b8Sblymn
306eb5227b8Sblymn /*
3074307e10dSad * Return non-zero if a device has an I/O request in flight.
3084307e10dSad */
3094307e10dSad bool
iostat_isbusy(struct io_stats * stats)3104307e10dSad iostat_isbusy(struct io_stats *stats)
3114307e10dSad {
3124307e10dSad
3134307e10dSad return stats->io_busy != 0;
3144307e10dSad }
3154307e10dSad
3164307e10dSad /*
317eb5227b8Sblymn * Increment the seek counter. This does look almost redundant but it
318eb5227b8Sblymn * abstracts the stats gathering.
319eb5227b8Sblymn */
320eb5227b8Sblymn void
iostat_seek(struct io_stats * stats)321eb5227b8Sblymn iostat_seek(struct io_stats *stats)
322eb5227b8Sblymn {
3236237cabcSyamt
32410df330cSblymn stats->io_seek++;
325eb5227b8Sblymn }
326eb5227b8Sblymn
327eb5227b8Sblymn static int
sysctl_hw_disknames(SYSCTLFN_ARGS)328eb5227b8Sblymn sysctl_hw_disknames(SYSCTLFN_ARGS)
329eb5227b8Sblymn {
3306237cabcSyamt
331eb5227b8Sblymn return iostati_getnames(1, oldp, oldlenp, newp, namelen);
332eb5227b8Sblymn }
333eb5227b8Sblymn
334eb5227b8Sblymn static int
sysctl_hw_iostatnames(SYSCTLFN_ARGS)335eb5227b8Sblymn sysctl_hw_iostatnames(SYSCTLFN_ARGS)
336eb5227b8Sblymn {
3376237cabcSyamt
338eb5227b8Sblymn return iostati_getnames(0, oldp, oldlenp, newp, namelen);
339eb5227b8Sblymn }
340eb5227b8Sblymn
341eb5227b8Sblymn static int
iostati_getnames(int disk_only,char * oldp,size_t * oldlenp,const void * newp,u_int namelen)342eb5227b8Sblymn iostati_getnames(int disk_only, char *oldp, size_t *oldlenp, const void *newp,
343eb5227b8Sblymn u_int namelen)
344eb5227b8Sblymn {
345eb5227b8Sblymn char bf[IOSTATNAMELEN + 1];
346eb5227b8Sblymn char *where = oldp;
347eb5227b8Sblymn struct io_stats *stats;
348eb5227b8Sblymn size_t needed, left, slen;
349eb5227b8Sblymn int error, first;
350eb5227b8Sblymn
351eb5227b8Sblymn if (newp != NULL)
352eb5227b8Sblymn return (EPERM);
353eb5227b8Sblymn if (namelen != 0)
354eb5227b8Sblymn return (EINVAL);
355eb5227b8Sblymn
356eb5227b8Sblymn first = 1;
357eb5227b8Sblymn error = 0;
358eb5227b8Sblymn needed = 0;
359eb5227b8Sblymn left = *oldlenp;
360eb5227b8Sblymn
361b07ec3fcSad rw_enter(&iostatlist_lock, RW_READER);
362eb5227b8Sblymn for (stats = TAILQ_FIRST(&iostatlist); stats != NULL;
36310df330cSblymn stats = TAILQ_NEXT(stats, io_link)) {
36410df330cSblymn if ((disk_only == 1) && (stats->io_type != IOSTAT_DISK))
365eb5227b8Sblymn continue;
366eb5227b8Sblymn
367eb5227b8Sblymn if (where == NULL)
36810df330cSblymn needed += strlen(stats->io_name) + 1;
369eb5227b8Sblymn else {
370eb5227b8Sblymn memset(bf, 0, sizeof(bf));
371eb5227b8Sblymn if (first) {
37210df330cSblymn strncpy(bf, stats->io_name, sizeof(bf));
373*9272c79bSmlelstv /* account for trailing NUL byte */
374*9272c79bSmlelstv needed += 1;
375eb5227b8Sblymn first = 0;
376eb5227b8Sblymn } else {
377eb5227b8Sblymn bf[0] = ' ';
37810df330cSblymn strncpy(bf + 1, stats->io_name,
379eb5227b8Sblymn sizeof(bf) - 1);
380eb5227b8Sblymn }
381eb5227b8Sblymn bf[IOSTATNAMELEN] = '\0';
382eb5227b8Sblymn slen = strlen(bf);
383eb5227b8Sblymn if (left < slen + 1)
384eb5227b8Sblymn break;
385eb5227b8Sblymn /* +1 to copy out the trailing NUL byte */
386eb5227b8Sblymn error = copyout(bf, where, slen + 1);
387eb5227b8Sblymn if (error)
388eb5227b8Sblymn break;
389eb5227b8Sblymn where += slen;
390eb5227b8Sblymn needed += slen;
391eb5227b8Sblymn left -= slen;
392eb5227b8Sblymn }
393eb5227b8Sblymn }
394b07ec3fcSad rw_exit(&iostatlist_lock);
395eb5227b8Sblymn *oldlenp = needed;
396eb5227b8Sblymn return (error);
397eb5227b8Sblymn }
398eb5227b8Sblymn
399eb5227b8Sblymn static int
sysctl_hw_iostats(SYSCTLFN_ARGS)400eb5227b8Sblymn sysctl_hw_iostats(SYSCTLFN_ARGS)
401eb5227b8Sblymn {
402eb5227b8Sblymn struct io_sysctl sdrive;
403eb5227b8Sblymn struct io_stats *stats;
404eb5227b8Sblymn char *where = oldp;
405eb5227b8Sblymn size_t tocopy, left;
406eb5227b8Sblymn int error;
407eb5227b8Sblymn
408eb5227b8Sblymn if (newp != NULL)
409eb5227b8Sblymn return (EPERM);
410eb5227b8Sblymn
411eb5227b8Sblymn /*
412eb5227b8Sblymn * The original hw.diskstats call was broken and did not require
413f0a7346dSsnj * the userland to pass in its size of struct disk_sysctl. This
414ef621e33Sad * was fixed after NetBSD 1.6 was released.
415eb5227b8Sblymn */
416eb5227b8Sblymn if (namelen == 0)
417eb5227b8Sblymn tocopy = offsetof(struct io_sysctl, busy);
418eb5227b8Sblymn else
419eb5227b8Sblymn tocopy = name[0];
420eb5227b8Sblymn
421eb5227b8Sblymn if (where == NULL) {
422eb5227b8Sblymn *oldlenp = iostat_count * tocopy;
423eb5227b8Sblymn return (0);
424eb5227b8Sblymn }
425eb5227b8Sblymn
426eb5227b8Sblymn error = 0;
427eb5227b8Sblymn left = *oldlenp;
428eb5227b8Sblymn memset(&sdrive, 0, sizeof(sdrive));
429eb5227b8Sblymn *oldlenp = 0;
430eb5227b8Sblymn
431b07ec3fcSad rw_enter(&iostatlist_lock, RW_READER);
43210df330cSblymn TAILQ_FOREACH(stats, &iostatlist, io_link) {
433eb5227b8Sblymn if (left < tocopy)
434eb5227b8Sblymn break;
435ba576b71Smlelstv
43610df330cSblymn strncpy(sdrive.name, stats->io_name, sizeof(sdrive.name));
43710df330cSblymn sdrive.attachtime_sec = stats->io_attachtime.tv_sec;
43810df330cSblymn sdrive.attachtime_usec = stats->io_attachtime.tv_usec;
439ba576b71Smlelstv sdrive.timestamp_sec = stats->io_busystamp.tv_sec;
440ba576b71Smlelstv sdrive.timestamp_usec = stats->io_busystamp.tv_usec;
441ba576b71Smlelstv
442ba576b71Smlelstv sdrive.time_sec = stats->io_busytime.tv_sec;
443ba576b71Smlelstv sdrive.time_usec = stats->io_busytime.tv_usec;
444ba576b71Smlelstv
445ba576b71Smlelstv sdrive.seek = stats->io_seek;
446ba576b71Smlelstv
447ba576b71Smlelstv sdrive.rxfer = stats->io_rxfer;
448ba576b71Smlelstv sdrive.wxfer = stats->io_wxfer;
449ba576b71Smlelstv sdrive.xfer = stats->io_rxfer + stats->io_wxfer;
450ba576b71Smlelstv
451ba576b71Smlelstv sdrive.rbytes = stats->io_rbytes;
452ba576b71Smlelstv sdrive.wbytes = stats->io_wbytes;
453ba576b71Smlelstv sdrive.bytes = stats->io_rbytes + stats->io_wbytes;
454ba576b71Smlelstv
455ba576b71Smlelstv sdrive.wait_sec = stats->io_waittime.tv_sec;
456ba576b71Smlelstv sdrive.wait_usec = stats->io_waittime.tv_usec;
457ba576b71Smlelstv
458ba576b71Smlelstv sdrive.time_sec = stats->io_busytime.tv_sec;
459ba576b71Smlelstv sdrive.time_usec = stats->io_busytime.tv_usec;
460ba576b71Smlelstv
461ba576b71Smlelstv sdrive.waitsum_sec = stats->io_waitsum.tv_sec;
462ba576b71Smlelstv sdrive.waitsum_usec = stats->io_waitsum.tv_usec;
463ba576b71Smlelstv
464ba576b71Smlelstv sdrive.busysum_sec = stats->io_busysum.tv_sec;
465ba576b71Smlelstv sdrive.busysum_usec = stats->io_busysum.tv_usec;
466ba576b71Smlelstv
46710df330cSblymn sdrive.busy = stats->io_busy;
468eb5227b8Sblymn
469d1579b2dSriastradh error = copyout(&sdrive, where, uimin(tocopy, sizeof(sdrive)));
470eb5227b8Sblymn if (error)
471eb5227b8Sblymn break;
472eb5227b8Sblymn where += tocopy;
473eb5227b8Sblymn *oldlenp += tocopy;
474eb5227b8Sblymn left -= tocopy;
475eb5227b8Sblymn }
476b07ec3fcSad rw_exit(&iostatlist_lock);
477eb5227b8Sblymn return (error);
478eb5227b8Sblymn }
479eb5227b8Sblymn
48011281f01Spooka static void
sysctl_io_stats_setup(struct sysctllog ** clog)48111281f01Spooka sysctl_io_stats_setup(struct sysctllog **clog)
482eb5227b8Sblymn {
4836237cabcSyamt
484eb5227b8Sblymn sysctl_createv(clog, 0, NULL, NULL,
485eb5227b8Sblymn CTLFLAG_PERMANENT,
486eb5227b8Sblymn CTLTYPE_STRING, "disknames",
487eb5227b8Sblymn SYSCTL_DESCR("List of disk drives present"),
488eb5227b8Sblymn sysctl_hw_disknames, 0, NULL, 0,
489eb5227b8Sblymn CTL_HW, HW_DISKNAMES, CTL_EOL);
490eb5227b8Sblymn sysctl_createv(clog, 0, NULL, NULL,
491eb5227b8Sblymn CTLFLAG_PERMANENT,
492eb5227b8Sblymn CTLTYPE_STRING, "iostatnames",
493eb5227b8Sblymn SYSCTL_DESCR("I/O stats are being collected for these"
494eb5227b8Sblymn " devices"),
495eb5227b8Sblymn sysctl_hw_iostatnames, 0, NULL, 0,
496eb5227b8Sblymn CTL_HW, HW_IOSTATNAMES, CTL_EOL);
497eb5227b8Sblymn sysctl_createv(clog, 0, NULL, NULL,
498eb5227b8Sblymn CTLFLAG_PERMANENT,
499c1078a4aSyamt CTLTYPE_STRUCT, "iostats",
500eb5227b8Sblymn SYSCTL_DESCR("Statistics on device I/O operations"),
501eb5227b8Sblymn sysctl_hw_iostats, 0, NULL, 0,
502eb5227b8Sblymn CTL_HW, HW_IOSTATS, CTL_EOL);
503eb5227b8Sblymn }
504