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