xref: /netbsd-src/sys/kern/subr_iostat.c (revision 9272c79b768240ff4e11f6b6dc9fa42cb6dd9177)
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