netbsd/sys/dev/iscsi/iscsi_profile.c
2013-04-06 16:48:33 +02:00

252 lines
6.1 KiB
C

/* $NetBSD: iscsi_profile.c,v 1.1 2011/10/23 21:15:02 agc Exp $ */
/*-
* Copyright (c) 2005,2006,2011 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Wasabi Systems, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "iscsi_globals.h"
#ifdef ISCSI_PERFTEST
command_perf_t *perfdata = NULL;
int perf_active = 0;
static int perfcount = 0;
static int perfindex = 0;
static uint64_t ticks_per_100us = 0;
/*
* perf_begin_command:
* Start command profiling.
* Allocates space for a command entry, sets the data length and
* flags, and gets the begin cycle count.
* If no room is left in the data buffer, nothing is set.
*
* Note: The perf_index set in the CCB is the index + 1, so that 0 is
* reserved as profiling not active (this is the default state
* for CCB fields).
*
* This function must only be called if perf_active is nonzero.
* The wrapper macro checks for this condition.
*
* Parameter:
* ccb The associated CCB.
* nowait Nonzero if this is a no-wait command.
*/
void
perf_begin_command(ccb_t *ccb, int nowait)
{
int idx;
/* Only trace commands that actually transfer data so we don't skew results */
if (ccb->data_len < 512) {
return;
}
idx = perfindex++;
if (idx >= perfcount) {
--perfindex;
return;
}
perfdata[idx].datalen = ccb->data_len;
if (ccb->data_in) {
perfdata[idx].status |= PERF_FLG_READ;
}
if (nowait) {
perfdata[idx].status |= PERF_FLG_NOWAIT;
}
ccb->perf_index = ++idx;
perf_snap(idx, PERF_BEGIN_COMMAND);
}
/*
* perf_end_command:
* Ends command profiling.
* Gets the end cycle count, and sets the complete flag.
*
* Parameter:
* ccb The associated CCB.
*/
void
perf_end_command(ccb_t *ccb)
{
int idx = ccb->perf_index;
perf_snap(idx, PERF_END_COMMAND);
perfdata[idx - 1].status |=
(ccb->status & 0xffffff) | PERF_FLG_COMPLETE;
ccb->perf_index = 0;
}
/*
* perf_do_stop:
* Stops performance data collection (internal function).
* Clears perf_active, and waits until all commands have completed.
* The wait is limited to 5 seconds.
*/
STATIC void
perf_do_stop(void)
{
int i, n = 0;
perf_active = 0;
do {
for (i = 0; i < perfindex; i++) {
if (!(perfdata[i].status & PERF_FLG_COMPLETE)) {
break;
}
}
if (i < perfindex) {
tsleep(&perfindex, PWAIT, "wait_perf", 1 * hz);
}
} while (n++ < 10 && i < perfindex);
}
/*
* perf_start:
* Starts performance data collection.
* If collection is already running, it is stopped.
* Allocates a buffer if necessary, clears the buffer, resets indices.
* Measures tick timing.
*
* Parameter:
* par The ioctl parameter specifying the number of elements.
*/
void
perf_start(iscsi_perf_startstop_parameters_t *par)
{
uint64_t rv, rv1, rv2;
int i, s;
if (par->num_elements <= 0) {
par->status = ISCSI_STATUS_PARAMETER_INVALID;
return;
}
if (perf_active)
perf_do_stop();
if (perfdata == NULL || perfcount < par->num_elements) {
if (perfdata != NULL) {
free(perfdata, M_DEVBUF);
}
perfdata = malloc(par->num_elements * sizeof(*perfdata),
M_DEVBUF, M_WAITOK);
if (perfdata == NULL) {
perfcount = 0;
par->status = ISCSI_STATUS_NO_RESOURCES;
return;
}
perfcount = par->num_elements;
}
(void) memset(perfdata, 0x0, perfcount * sizeof(*perfdata));
perfindex = 0;
rv = 0;
for (i = 0; i < 5; i++) {
s = splhigh();
__asm __volatile("rdtsc":"=A"(rv1));
delay(100);
__asm __volatile("rdtsc":"=A"(rv2));
splx(s);
rv += rv2 - rv1;
}
ticks_per_100us = rv / 5;
par->status = ISCSI_STATUS_SUCCESS;
perf_active = 1;
}
/*
* perf_start:
* Stops performance data collection (external interface).
*
* Parameter:
* par The ioctl parameter receiving the number of
* elements collected.
*/
void
perf_stop(iscsi_perf_startstop_parameters_t *par)
{
if (perfdata == NULL || perfcount <= 0 || !perf_active) {
par->status = ISCSI_STATUS_GENERAL_ERROR;
return;
}
perf_do_stop();
par->num_elements = perfindex;
par->status = ISCSI_STATUS_SUCCESS;
}
/*
* perf_get:
* Retrieves performance data.
*
* Parameter:
* par The ioctl parameter.
*/
void
perf_get(iscsi_perf_get_parameters_t *par)
{
int nelem;
if (perfdata == NULL || perfcount <= 0) {
par->status = ISCSI_STATUS_GENERAL_ERROR;
return;
}
par->status = ISCSI_STATUS_SUCCESS;
if (!par->buffersize) {
par->buffersize = perfindex * sizeof(command_perf_t);
par->num_elements = perfindex;
return;
}
nelem = par->buffersize / sizeof(command_perf_t);
if (!nelem) {
par->status = ISCSI_STATUS_PARAMETER_INVALID;
return;
}
nelem = min(nelem, perfindex);
copyout(perfdata, par->buffer, nelem * sizeof(command_perf_t));
par->num_elements = nelem;
par->ticks_per_100us = ticks_per_100us;
}
#endif