1/*	$NetBSD: iscsi_profile.c,v 1.1.1.1 2011/05/02 07:01:11 agc Exp $	*/
2
3/*-
4 * Copyright (c) 2005,2006,2011 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Wasabi Systems, Inc.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31#include "iscsi_globals.h"
32
33#ifdef ISCSI_PERFTEST
34
35command_perf_t *perfdata = NULL;
36int perf_active = 0;
37
38static int perfcount = 0;
39static int perfindex = 0;
40static uint64_t ticks_per_100us = 0;
41
42/*
43 * perf_begin_command:
44 *    Start command profiling.
45 *    Allocates space for a command entry, sets the data length and
46 *    flags, and gets the begin cycle count.
47 *    If no room is left in the data buffer, nothing is set.
48 *
49 *    Note: The perf_index set in the CCB is the index + 1, so that 0 is
50 *          reserved as profiling not active (this is the default state
51 *          for CCB fields).
52 *
53 *    This function must only be called if perf_active is nonzero.
54 *    The wrapper macro checks for this condition.
55 *
56 *    Parameter:
57 *          ccb      The associated CCB.
58 *          nowait   Nonzero if this is a no-wait command.
59 */
60
61void
62perf_begin_command(ccb_t *ccb, int nowait)
63{
64	int idx;
65
66	/* Only trace commands that actually transfer data so we don't skew results */
67	if (ccb->data_len < 512) {
68		return;
69	}
70	idx = perfindex++;
71	if (idx >= perfcount) {
72		--perfindex;
73		return;
74	}
75	perfdata[idx].datalen = ccb->data_len;
76	if (ccb->data_in) {
77		perfdata[idx].status |= PERF_FLG_READ;
78	}
79	if (nowait) {
80		perfdata[idx].status |= PERF_FLG_NOWAIT;
81	}
82	ccb->perf_index = ++idx;
83	perf_snap(idx, PERF_BEGIN_COMMAND);
84}
85
86
87/*
88 * perf_end_command:
89 *    Ends command profiling.
90 *    Gets the end cycle count, and sets the complete flag.
91 *
92 *    Parameter:
93 *          ccb      The associated CCB.
94 */
95
96void
97perf_end_command(ccb_t *ccb)
98{
99	int idx = ccb->perf_index;
100
101	perf_snap(idx, PERF_END_COMMAND);
102	perfdata[idx - 1].status |=
103			(ccb->status & 0xffffff) | PERF_FLG_COMPLETE;
104	ccb->perf_index = 0;
105}
106
107
108/*
109 * perf_do_stop:
110 *    Stops performance data collection (internal function).
111 *    Clears perf_active, and waits until all commands have completed.
112 *    The wait is limited to 5 seconds.
113 */
114
115STATIC void
116perf_do_stop(void)
117{
118	int i, n = 0;
119
120	perf_active = 0;
121	do {
122		for (i = 0; i < perfindex; i++) {
123			if (!(perfdata[i].status & PERF_FLG_COMPLETE)) {
124				break;
125			}
126		}
127		if (i < perfindex) {
128			tsleep(&perfindex, PWAIT, "wait_perf", 1 * hz);
129		}
130	} while (n++ < 10 && i < perfindex);
131}
132
133
134/*
135 * perf_start:
136 *    Starts performance data collection.
137 *    If collection is already running, it is stopped.
138 *    Allocates a buffer if necessary, clears the buffer, resets indices.
139 *    Measures tick timing.
140 *
141 *    Parameter:
142 *          par      The ioctl parameter specifying the number of elements.
143 */
144
145void
146perf_start(iscsi_perf_startstop_parameters_t *par)
147{
148	uint64_t rv, rv1, rv2;
149	int i, s;
150
151	if (par->num_elements <= 0) {
152		par->status = ISCSI_STATUS_PARAMETER_INVALID;
153		return;
154	}
155	if (perf_active)
156		perf_do_stop();
157
158	if (perfdata == NULL || perfcount < par->num_elements) {
159		if (perfdata != NULL) {
160			free(perfdata, M_DEVBUF);
161		}
162		perfdata = malloc(par->num_elements * sizeof(*perfdata),
163					M_DEVBUF, M_WAITOK);
164		if (perfdata == NULL) {
165			perfcount = 0;
166			par->status = ISCSI_STATUS_NO_RESOURCES;
167			return;
168		}
169		perfcount = par->num_elements;
170	}
171
172	(void) memset(perfdata, 0x0, perfcount * sizeof(*perfdata));
173	perfindex = 0;
174
175	rv = 0;
176	for (i = 0; i < 5; i++) {
177		s = splhigh();
178
179		__asm __volatile("rdtsc":"=A"(rv1));
180		delay(100);
181		__asm __volatile("rdtsc":"=A"(rv2));
182
183		splx(s);
184		rv += rv2 - rv1;
185	}
186
187	ticks_per_100us = rv / 5;
188	par->status = ISCSI_STATUS_SUCCESS;
189
190	perf_active = 1;
191}
192
193
194/*
195 * perf_start:
196 *    Stops performance data collection (external interface).
197 *
198 *    Parameter:
199 *          par      The ioctl parameter receiving the number of
200 *                   elements collected.
201 */
202
203void
204perf_stop(iscsi_perf_startstop_parameters_t *par)
205{
206	if (perfdata == NULL || perfcount <= 0 || !perf_active) {
207		par->status = ISCSI_STATUS_GENERAL_ERROR;
208		return;
209	}
210	perf_do_stop();
211	par->num_elements = perfindex;
212	par->status = ISCSI_STATUS_SUCCESS;
213}
214
215
216/*
217 * perf_get:
218 *    Retrieves performance data.
219 *
220 *    Parameter:
221 *          par      The ioctl parameter.
222 */
223
224void
225perf_get(iscsi_perf_get_parameters_t *par)
226{
227	int nelem;
228
229	if (perfdata == NULL || perfcount <= 0) {
230		par->status = ISCSI_STATUS_GENERAL_ERROR;
231		return;
232	}
233	par->status = ISCSI_STATUS_SUCCESS;
234
235	if (!par->buffersize) {
236		par->buffersize = perfindex * sizeof(command_perf_t);
237		par->num_elements = perfindex;
238		return;
239	}
240	nelem = par->buffersize / sizeof(command_perf_t);
241	if (!nelem) {
242		par->status = ISCSI_STATUS_PARAMETER_INVALID;
243		return;
244	}
245	nelem = min(nelem, perfindex);
246	copyout(perfdata, par->buffer, nelem * sizeof(command_perf_t));
247	par->num_elements = nelem;
248	par->ticks_per_100us = ticks_per_100us;
249}
250
251#endif
252