xref: /netbsd-src/sys/kern/subr_iostat.c (revision cd22f25e6f6d1cc1f197fe8c5468a80f51d1c4e1)
1 /*	$NetBSD: subr_iostat.c,v 1.14 2008/04/28 20:24:04 martin Exp $	*/
2 /*	NetBSD: subr_disk.c,v 1.69 2005/05/29 22:24:15 christos Exp	*/
3 
4 /*-
5  * Copyright (c) 1996, 1997, 1999, 2000 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
10  * NASA Ames Research Center.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
25  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 /*
35  * Copyright (c) 1982, 1986, 1988, 1993
36  *	The Regents of the University of California.  All rights reserved.
37  * (c) UNIX System Laboratories, Inc.
38  * All or some portions of this file are derived from material licensed
39  * to the University of California by American Telephone and Telegraph
40  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
41  * the permission of UNIX System Laboratories, Inc.
42  *
43  * Redistribution and use in source and binary forms, with or without
44  * modification, are permitted provided that the following conditions
45  * are met:
46  * 1. Redistributions of source code must retain the above copyright
47  *    notice, this list of conditions and the following disclaimer.
48  * 2. Redistributions in binary form must reproduce the above copyright
49  *    notice, this list of conditions and the following disclaimer in the
50  *    documentation and/or other materials provided with the distribution.
51  * 3. Neither the name of the University nor the names of its contributors
52  *    may be used to endorse or promote products derived from this software
53  *    without specific prior written permission.
54  *
55  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
56  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
57  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
58  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
59  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
60  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
61  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
63  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
64  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
65  * SUCH DAMAGE.
66  *
67  *	@(#)ufs_disksubr.c	8.5 (Berkeley) 1/21/94
68  */
69 
70 #include <sys/cdefs.h>
71 __KERNEL_RCSID(0, "$NetBSD: subr_iostat.c,v 1.14 2008/04/28 20:24:04 martin Exp $");
72 
73 #include "opt_compat_netbsd.h"
74 
75 #include <sys/param.h>
76 #include <sys/kernel.h>
77 #include <sys/malloc.h>
78 #include <sys/iostat.h>
79 #include <sys/sysctl.h>
80 #include <sys/rwlock.h>
81 
82 /*
83  * Function prototypes for sysctl nodes
84  */
85 static int	sysctl_hw_disknames(SYSCTLFN_PROTO);
86 static int	sysctl_hw_iostatnames(SYSCTLFN_PROTO);
87 static int	sysctl_hw_iostats(SYSCTLFN_PROTO);
88 
89 static int
90 iostati_getnames(int disk_only, char *oldp, size_t *oldlenp, const void *newp,
91 		u_int namelen);
92 
93 /*
94  * A global list of all drives attached to the system.  May grow or
95  * shrink over time.
96  */
97 struct iostatlist_head iostatlist = TAILQ_HEAD_INITIALIZER(iostatlist);
98 int iostat_count;		/* number of drives in global drivelist */
99 krwlock_t iostatlist_lock;
100 
101 /*
102  * Initialise the iostat subsystem.
103  */
104 void
105 iostat_init(void)
106 {
107 
108 	rw_init(&iostatlist_lock);
109 }
110 
111 /*
112  * Searches the iostatlist for the iostat corresponding to the
113  * name provided.
114  */
115 struct io_stats *
116 iostat_find(const char *name)
117 {
118 	struct io_stats *iostatp;
119 
120 	KASSERT(name != NULL);
121 
122 	rw_enter(&iostatlist_lock, RW_READER);
123 	TAILQ_FOREACH(iostatp, &iostatlist, io_link) {
124 		if (strcmp(iostatp->io_name, name) == 0) {
125 			break;
126 		}
127 	}
128 	rw_exit(&iostatlist_lock);
129 
130 	return iostatp;
131 }
132 
133 /*
134  * Allocate and initialise memory for the i/o statistics.
135  */
136 struct io_stats *
137 iostat_alloc(int32_t type, void *parent, const char *name)
138 {
139 	struct io_stats *stats;
140 
141 	stats = malloc(sizeof(struct io_stats), M_DEVBUF, M_WAITOK|M_ZERO);
142 	if (stats == NULL)
143 		panic("iostat_alloc: cannot allocate memory for stats buffer");
144 
145 	stats->io_type = type;
146 	stats->io_parent = parent;
147 	(void)strlcpy(stats->io_name, name, sizeof(stats->io_name));
148 
149 	/*
150 	 * Set the attached timestamp.
151 	 */
152 	getmicrouptime(&stats->io_attachtime);
153 
154 	/*
155 	 * Link into the drivelist.
156 	 */
157 	rw_enter(&iostatlist_lock, RW_WRITER);
158 	TAILQ_INSERT_TAIL(&iostatlist, stats, io_link);
159 	iostat_count++;
160 	rw_exit(&iostatlist_lock);
161 
162 	return stats;
163 }
164 
165 /*
166  * Remove i/o from stats collection.
167  */
168 void
169 iostat_free(struct io_stats *stats)
170 {
171 
172 	/*
173 	 * Remove from the iostat list.
174 	 */
175 	if (iostat_count == 0)
176 		panic("iostat_free: iostat_count == 0");
177 	rw_enter(&iostatlist_lock, RW_WRITER);
178 	TAILQ_REMOVE(&iostatlist, stats, io_link);
179 	iostat_count--;
180 	rw_exit(&iostatlist_lock);
181 	free(stats, M_DEVBUF);
182 }
183 
184 /*
185  * Increment a iostat busy counter.  If the counter is going from
186  * 0 to 1, set the timestamp.
187  */
188 void
189 iostat_busy(struct io_stats *stats)
190 {
191 
192 	if (stats->io_busy++ == 0)
193 		getmicrouptime(&stats->io_timestamp);
194 }
195 
196 /*
197  * Decrement a iostat busy counter, increment the byte count, total busy
198  * time, and reset the timestamp.
199  */
200 void
201 iostat_unbusy(struct io_stats *stats, long bcount, int read)
202 {
203 	struct timeval dv_time, diff_time;
204 
205 	if (stats->io_busy-- == 0) {
206 		printf("%s: busy < 0\n", stats->io_name);
207 		panic("iostat_unbusy");
208 	}
209 
210 	getmicrouptime(&dv_time);
211 
212 	timersub(&dv_time, &stats->io_timestamp, &diff_time);
213 	timeradd(&stats->io_time, &diff_time, &stats->io_time);
214 
215 	stats->io_timestamp = dv_time;
216 	if (bcount > 0) {
217 		if (read) {
218 			stats->io_rbytes += bcount;
219 			stats->io_rxfer++;
220 		} else {
221 			stats->io_wbytes += bcount;
222 			stats->io_wxfer++;
223 		}
224 	}
225 }
226 
227 /*
228  * Increment the seek counter.  This does look almost redundant but it
229  * abstracts the stats gathering.
230  */
231 void
232 iostat_seek(struct io_stats *stats)
233 {
234 
235 	stats->io_seek++;
236 }
237 
238 static int
239 sysctl_hw_disknames(SYSCTLFN_ARGS)
240 {
241 
242 	return iostati_getnames(1, oldp, oldlenp, newp, namelen);
243 }
244 
245 static int
246 sysctl_hw_iostatnames(SYSCTLFN_ARGS)
247 {
248 
249 	return iostati_getnames(0, oldp, oldlenp, newp, namelen);
250 }
251 
252 static int
253 iostati_getnames(int disk_only, char *oldp, size_t *oldlenp, const void *newp,
254 		 u_int namelen)
255 {
256 	char bf[IOSTATNAMELEN + 1];
257 	char *where = oldp;
258 	struct io_stats *stats;
259 	size_t needed, left, slen;
260 	int error, first;
261 
262 	if (newp != NULL)
263 		return (EPERM);
264 	if (namelen != 0)
265 		return (EINVAL);
266 
267 	first = 1;
268 	error = 0;
269 	needed = 0;
270 	left = *oldlenp;
271 
272 	rw_enter(&iostatlist_lock, RW_READER);
273 	for (stats = TAILQ_FIRST(&iostatlist); stats != NULL;
274 	    stats = TAILQ_NEXT(stats, io_link)) {
275 		if ((disk_only == 1) && (stats->io_type != IOSTAT_DISK))
276 			continue;
277 
278 		if (where == NULL)
279 			needed += strlen(stats->io_name) + 1;
280 		else {
281 			memset(bf, 0, sizeof(bf));
282 			if (first) {
283 				strncpy(bf, stats->io_name, sizeof(bf));
284 				first = 0;
285 			} else {
286 				bf[0] = ' ';
287 				strncpy(bf + 1, stats->io_name,
288 				    sizeof(bf) - 1);
289 			}
290 			bf[IOSTATNAMELEN] = '\0';
291 			slen = strlen(bf);
292 			if (left < slen + 1)
293 				break;
294 			/* +1 to copy out the trailing NUL byte */
295 			error = copyout(bf, where, slen + 1);
296 			if (error)
297 				break;
298 			where += slen;
299 			needed += slen;
300 			left -= slen;
301 		}
302 	}
303 	rw_exit(&iostatlist_lock);
304 	*oldlenp = needed;
305 	return (error);
306 }
307 
308 static int
309 sysctl_hw_iostats(SYSCTLFN_ARGS)
310 {
311 	struct io_sysctl sdrive;
312 	struct io_stats *stats;
313 	char *where = oldp;
314 	size_t tocopy, left;
315 	int error;
316 
317 	if (newp != NULL)
318 		return (EPERM);
319 
320 	/*
321 	 * The original hw.diskstats call was broken and did not require
322 	 * the userland to pass in it's size of struct disk_sysctl.  This
323 	 * was fixed after NetBSD 1.6 was released, and any applications
324 	 * that do not pass in the size are given an error only, unless
325 	 * we care about 1.6 compatibility.
326 	 */
327 	if (namelen == 0)
328 #ifdef COMPAT_16
329 		tocopy = offsetof(struct io_sysctl, busy);
330 #else
331 		return (EINVAL);
332 #endif
333 	else
334 		tocopy = name[0];
335 
336 	if (where == NULL) {
337 		*oldlenp = iostat_count * tocopy;
338 		return (0);
339 	}
340 
341 	error = 0;
342 	left = *oldlenp;
343 	memset(&sdrive, 0, sizeof(sdrive));
344 	*oldlenp = 0;
345 
346 	rw_enter(&iostatlist_lock, RW_READER);
347 	TAILQ_FOREACH(stats, &iostatlist, io_link) {
348 		if (left < tocopy)
349 			break;
350 		strncpy(sdrive.name, stats->io_name, sizeof(sdrive.name));
351 		sdrive.xfer = stats->io_rxfer + stats->io_wxfer;
352 		sdrive.rxfer = stats->io_rxfer;
353 		sdrive.wxfer = stats->io_wxfer;
354 		sdrive.seek = stats->io_seek;
355 		sdrive.bytes = stats->io_rbytes + stats->io_wbytes;
356 		sdrive.rbytes = stats->io_rbytes;
357 		sdrive.wbytes = stats->io_wbytes;
358 		sdrive.attachtime_sec = stats->io_attachtime.tv_sec;
359 		sdrive.attachtime_usec = stats->io_attachtime.tv_usec;
360 		sdrive.timestamp_sec = stats->io_timestamp.tv_sec;
361 		sdrive.timestamp_usec = stats->io_timestamp.tv_usec;
362 		sdrive.time_sec = stats->io_time.tv_sec;
363 		sdrive.time_usec = stats->io_time.tv_usec;
364 		sdrive.busy = stats->io_busy;
365 
366 		error = copyout(&sdrive, where, min(tocopy, sizeof(sdrive)));
367 		if (error)
368 			break;
369 		where += tocopy;
370 		*oldlenp += tocopy;
371 		left -= tocopy;
372 	}
373 	rw_exit(&iostatlist_lock);
374 	return (error);
375 }
376 
377 SYSCTL_SETUP(sysctl_io_stats_setup, "sysctl i/o stats setup")
378 {
379 
380 	sysctl_createv(clog, 0, NULL, NULL,
381 		       CTLFLAG_PERMANENT,
382 		       CTLTYPE_STRING, "disknames",
383 		       SYSCTL_DESCR("List of disk drives present"),
384 		       sysctl_hw_disknames, 0, NULL, 0,
385 		       CTL_HW, HW_DISKNAMES, CTL_EOL);
386 	sysctl_createv(clog, 0, NULL, NULL,
387 		       CTLFLAG_PERMANENT,
388 		       CTLTYPE_STRING, "iostatnames",
389 		       SYSCTL_DESCR("I/O stats are being collected for these"
390 				    " devices"),
391 		       sysctl_hw_iostatnames, 0, NULL, 0,
392 		       CTL_HW, HW_IOSTATNAMES, CTL_EOL);
393 	sysctl_createv(clog, 0, NULL, NULL,
394 		       CTLFLAG_PERMANENT,
395 		       CTLTYPE_STRUCT, "iostats",
396 		       SYSCTL_DESCR("Statistics on device I/O operations"),
397 		       sysctl_hw_iostats, 0, NULL, 0,
398 		       CTL_HW, HW_IOSTATS, CTL_EOL);
399 }
400