mirror of
https://github.com/SCST-project/scst.git
synced 2026-08-19 13:46:25 +00:00
scst: Add a new latency measurement infrastructure
An example of the data produced by this infrastructure: $ echo 1 >/sys/kernel/scst_tgt/measure_latency $ sleep 10 # Wait until an initiator has submitted multiple I/O requests $ cat /sys/kernel/scst_tgt/targets/ib_srpt/fe80\:0000\:0000\:0000\:0002\:c903\:00fa\:b7f2/sessions/fe80\:0000\:0000\:0000\:0002\:c903\:00a3\:4272/latency/r512 state count min max avg stddev PARSE 219 1.3 26.6 2.2 2.5 us PREPARE_SPACE 219 0.9 10.3 1.1 0.6 us RDY_TO_XFER 219 0.7 1.7 0.7 0.2 us TGT_PRE_EXEC 219 0.7 11.0 0.8 0.9 us EXEC_CHECK_SN 219 0.7 1.7 0.8 0.2 us PRE_DEV_DONE 219 11.3 3445.7 39.6 276.4 us DEV_DONE 219 0.7 11.0 0.9 0.7 us PRE_XMIT_RESP1 219 1.2 58.4 1.6 3.8 us CSW2 219 0.7 1.6 0.8 0.1 us PRE_XMIT_RESP2 219 0.7 1.5 0.7 0.1 us XMIT_RESP 219 0.7 1.5 0.7 0.1 us INIT_WAIT 219 1.0 57.3 2.1 4.4 us INIT 219 0.9 27.4 1.6 2.4 us CSW1 219 15.0 3856.1 74.2 264.8 us EXEC_CHECK_BLOCKING 219 1.3 10.8 1.7 0.9 us LOCAL_EXEC 219 0.7 1.8 0.7 0.1 us REAL_EXEC 219 0.6 1.5 0.7 0.1 us EXEC_WAIT 219 40.6 1021.7 54.4 68.7 us XMIT_WAIT 219 6.4 1682.0 50.6 228.1 us total 219 - - 236.9 2012.1 us git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@7484 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
@@ -29,6 +29,7 @@
|
||||
#include <linux/slab.h> /* kmalloc() */
|
||||
#include <linux/timer.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/writeback.h> /* sync_page_range() */
|
||||
#include <rdma/ib_verbs.h>
|
||||
#include <scsi/scsi_cmnd.h> /* struct scsi_cmnd */
|
||||
@@ -63,6 +64,24 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* <asm/msr.h> */
|
||||
|
||||
#ifdef CONFIG_X86
|
||||
#include <asm/msr.h>
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0)
|
||||
static __always_inline unsigned long long rdtsc(void)
|
||||
{
|
||||
return native_read_tsc();
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
static __always_inline unsigned long long rdtsc(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#define tsc_khz 1000
|
||||
#endif
|
||||
|
||||
/* <linux/bio.h> */
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 13, 0)
|
||||
@@ -378,6 +397,23 @@ static inline int __must_check kstrtol(const char *s, unsigned int base,
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0)
|
||||
/* Suboptimal algorithm for computing the square root of a 64-bit number */
|
||||
static inline u32 int_sqrt64(u64 x)
|
||||
{
|
||||
u32 r = 0, s;
|
||||
int i;
|
||||
|
||||
for (i = 8 * sizeof(r) - 2; i >= 0;i --) {
|
||||
s = r + (1 << i);
|
||||
if (1ll * s * s <= x)
|
||||
r = s;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* <linux/kmod.h> */
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23)
|
||||
@@ -811,6 +847,20 @@ static inline void put_unaligned_be64(uint64_t i, void *p)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* <linux/vmalloc.h> */
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)
|
||||
static inline void *vzalloc(size_t size)
|
||||
{
|
||||
void *p;
|
||||
|
||||
p = vmalloc(size);
|
||||
if (p)
|
||||
memset(p, 0, size);
|
||||
return p;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* <scsi/scsi_cmnd.h> */
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
|
||||
|
||||
@@ -149,6 +149,8 @@ enum scst_cmd_state {
|
||||
/* Checks 1 before target driver's xmit_response() is called */
|
||||
SCST_CMD_STATE_PRE_XMIT_RESP1,
|
||||
|
||||
SCST_CMD_STATE_CSW2,
|
||||
|
||||
/* Checks 2 before target driver's xmit_response() is called */
|
||||
SCST_CMD_STATE_PRE_XMIT_RESP2,
|
||||
|
||||
@@ -172,6 +174,8 @@ enum scst_cmd_state {
|
||||
/* LUN translation (cmd->tgt_dev assignment) */
|
||||
SCST_CMD_STATE_INIT,
|
||||
|
||||
SCST_CMD_STATE_CSW1,
|
||||
|
||||
/* Waiting for scst_restart_cmd() */
|
||||
SCST_CMD_STATE_PREPROCESSING_DONE_CALLED,
|
||||
|
||||
@@ -197,6 +201,10 @@ enum scst_cmd_state {
|
||||
SCST_CMD_STATE_XMIT_WAIT,
|
||||
};
|
||||
|
||||
enum {
|
||||
SCST_CMD_STATE_COUNT = SCST_CMD_STATE_XMIT_WAIT + 1
|
||||
};
|
||||
|
||||
/*************************************************************
|
||||
* Can be returned instead of cmd's state by dev handlers'
|
||||
* functions, if the command's state should be set by default
|
||||
@@ -1844,6 +1852,57 @@ struct scst_tgt {
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* struct scst_lat_stat_entry - SCST command processing latency data
|
||||
* @last_update: Time of last update of this data structure in 100 ns.
|
||||
* @count: Number of samples for which statistics have been gathered.
|
||||
* @last_update_tsc: Time of the last update of this data structure in 100
|
||||
* clock cycles.
|
||||
* @min: Minimum processing time in nanoseconds.
|
||||
* @max: Maximum processing time.
|
||||
* @sum: Processing time sum.
|
||||
* @sumsq: Sum of the squares of the processing times.
|
||||
* @minc, @maxc, @sumc, @sumsqc: Similar to the above but in hundred clock
|
||||
* cycles instead of nanoseconds.
|
||||
*
|
||||
* Time unit for the uint64_t numbers: 100 ns.
|
||||
*
|
||||
* Size: 96 bytes.
|
||||
*/
|
||||
struct scst_lat_stat_entry {
|
||||
ktime_t last_update;
|
||||
uint32_t count;
|
||||
uint32_t padding;
|
||||
#ifdef SCST_MEASURE_CLOCK_CYCLES
|
||||
uint64_t last_update_tsc;
|
||||
#endif
|
||||
uint64_t min;
|
||||
uint64_t max;
|
||||
uint64_t sum;
|
||||
uint64_t sumsq;
|
||||
#ifdef SCST_MEASURE_CLOCK_CYCLES
|
||||
uint64_t minc;
|
||||
uint64_t maxc;
|
||||
uint64_t sumc;
|
||||
uint64_t sumsqc;
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* lat_stats is an array with three indices:
|
||||
* - Logarithm base 2 of the data length minus 9.
|
||||
* - Data direction (SCST_DATA_*).
|
||||
* - SCST command state.
|
||||
*
|
||||
* Size: 11 * 4 * 25 * 96 = 105600 bytes.
|
||||
*/
|
||||
#define SCST_STATS_LOG2_SZ_OFFSET 9
|
||||
#define SCST_STATS_MAX_LOG2_SZ 11
|
||||
struct scst_lat_stats {
|
||||
struct scst_lat_stat_entry
|
||||
ls[SCST_STATS_MAX_LOG2_SZ][4][SCST_CMD_STATE_COUNT];
|
||||
};
|
||||
|
||||
struct scst_io_stat_entry {
|
||||
uint64_t cmd_count;
|
||||
uint64_t io_byte_count;
|
||||
@@ -1971,6 +2030,7 @@ struct scst_session {
|
||||
unsigned int sess_kobj_ready:1;
|
||||
|
||||
struct kobject sess_kobj; /* session sysfs entry */
|
||||
struct kobject *lat_kobj;
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ -1981,6 +2041,12 @@ struct scst_session {
|
||||
void (*init_result_fn)(struct scst_session *sess, void *data,
|
||||
int result);
|
||||
void (*unreg_done_fn)(struct scst_session *sess);
|
||||
|
||||
/*
|
||||
* Latency measurement data.
|
||||
*/
|
||||
spinlock_t lat_stats_lock;
|
||||
struct scst_lat_stats *lat_stats;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -2318,6 +2384,9 @@ struct scst_cmd {
|
||||
|
||||
unsigned long start_time;
|
||||
|
||||
ktime_t init_wait_time;
|
||||
uint64_t init_wait_tsc;
|
||||
|
||||
/* List entry for tgt_dev's deferred (SN, ACA, etc.) lists */
|
||||
struct list_head deferred_cmd_list_entry;
|
||||
|
||||
|
||||
@@ -3063,6 +3063,7 @@ int scst_get_cmd_abnormal_done_state(struct scst_cmd *cmd)
|
||||
case SCST_CMD_STATE_INIT_WAIT:
|
||||
case SCST_CMD_STATE_INIT:
|
||||
case SCST_CMD_STATE_PARSE:
|
||||
case SCST_CMD_STATE_CSW1:
|
||||
if (cmd->preprocessing_only) {
|
||||
res = SCST_CMD_STATE_PREPROCESSING_DONE;
|
||||
break;
|
||||
@@ -3176,6 +3177,7 @@ void scst_set_cmd_abnormal_done_state(struct scst_cmd *cmd)
|
||||
case SCST_CMD_STATE_INIT_WAIT:
|
||||
case SCST_CMD_STATE_INIT:
|
||||
case SCST_CMD_STATE_PARSE:
|
||||
case SCST_CMD_STATE_CSW1:
|
||||
case SCST_CMD_STATE_PREPROCESSING_DONE:
|
||||
case SCST_CMD_STATE_PREPROCESSING_DONE_CALLED:
|
||||
case SCST_CMD_STATE_PREPARE_SPACE:
|
||||
@@ -6964,6 +6966,7 @@ struct scst_session *scst_alloc_session(struct scst_tgt *tgt, gfp_t gfp_mask,
|
||||
sess_cm_list_id_cleanup_work_fn, sess);
|
||||
INIT_WORK(&sess->hw_pending_work, scst_hw_pending_work_fn, sess);
|
||||
#endif
|
||||
spin_lock_init(&sess->lat_stats_lock);
|
||||
|
||||
sess->initiator_name = kstrdup(initiator_name, gfp_mask);
|
||||
if (sess->initiator_name == NULL) {
|
||||
@@ -6971,10 +6974,19 @@ struct scst_session *scst_alloc_session(struct scst_tgt *tgt, gfp_t gfp_mask,
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (atomic_read(&scst_measure_latency)) {
|
||||
sess->lat_stats = vzalloc(sizeof(*sess->lat_stats));
|
||||
if (!sess->lat_stats)
|
||||
goto out_free_name;
|
||||
}
|
||||
|
||||
out:
|
||||
TRACE_EXIT();
|
||||
return sess;
|
||||
|
||||
out_free_name:
|
||||
kfree(sess->initiator_name);
|
||||
|
||||
out_free:
|
||||
kmem_cache_free(scst_sess_cachep, sess);
|
||||
sess = NULL;
|
||||
@@ -7021,6 +7033,7 @@ void scst_free_session(struct scst_session *sess)
|
||||
mutex_unlock(&scst_mutex);
|
||||
|
||||
kfree(sess->transport_id);
|
||||
vfree(sess->lat_stats);
|
||||
kfree(sess->initiator_name);
|
||||
if (sess->sess_name != sess->initiator_name)
|
||||
kfree(sess->sess_name);
|
||||
@@ -15683,3 +15696,120 @@ void scst_check_debug_sn(struct scst_cmd *cmd)
|
||||
return;
|
||||
}
|
||||
#endif /* CONFIG_SCST_DEBUG_SN */
|
||||
|
||||
static void __scst_update_latency_stats(struct scst_cmd *cmd,
|
||||
struct scst_lat_stat_entry *stat,
|
||||
struct scst_lat_stat_entry *new_stat,
|
||||
const ktime_t now, uint64_t nowc)
|
||||
{
|
||||
int64_t delta;
|
||||
#ifdef SCST_MEASURE_CLOCK_CYCLES
|
||||
int64_t deltac;
|
||||
#endif
|
||||
|
||||
if (stat && stat->last_update) {
|
||||
delta = ktime_to_ns(ktime_sub(now, stat->last_update));
|
||||
if (delta < 0 || delta > NSEC_PER_SEC) {
|
||||
printk_once(KERN_INFO "%d: ignoring large time delta %lld\n",
|
||||
cmd->state, delta);
|
||||
delta = 0;
|
||||
}
|
||||
delta /= 100;
|
||||
#ifdef SCST_MEASURE_CLOCK_CYCLES
|
||||
deltac = nowc - stat->last_update_tsc;
|
||||
if (deltac < 0 || deltac > tsc_khz * 1000) {
|
||||
printk_once(KERN_INFO "%d: ignoring large cc delta %lld\n",
|
||||
cmd->state, deltac);
|
||||
deltac = 0;
|
||||
}
|
||||
deltac /= 100;
|
||||
#endif
|
||||
if (stat->count++ > 0) {
|
||||
if (delta < stat->min)
|
||||
stat->min = delta;
|
||||
if (delta > stat->max)
|
||||
stat->max = delta;
|
||||
#ifdef SCST_MEASURE_CLOCK_CYCLES
|
||||
if (deltac < stat->minc)
|
||||
stat->minc = deltac;
|
||||
if (deltac > stat->maxc)
|
||||
stat->maxc = deltac;
|
||||
#endif
|
||||
} else {
|
||||
stat->min = stat->max = delta;
|
||||
#ifdef SCST_MEASURE_CLOCK_CYCLES
|
||||
stat->minc = stat->maxc = deltac;
|
||||
#endif
|
||||
}
|
||||
stat->sum += delta;
|
||||
stat->sumsq += delta * delta;
|
||||
#ifdef SCST_MEASURE_CLOCK_CYCLES
|
||||
stat->sumc += deltac;
|
||||
stat->sumsqc += deltac * deltac;
|
||||
#endif
|
||||
}
|
||||
new_stat->last_update = now;
|
||||
#ifdef SCST_MEASURE_CLOCK_CYCLES
|
||||
new_stat->last_update_tsc = nowc;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: in the code below it has been assumed that expected_data_direction
|
||||
* and expected_transfer_len_full have been set before scst_cmd_init_done()
|
||||
* has been called and that these are not changed later on.
|
||||
*/
|
||||
void scst_update_latency_stats(struct scst_cmd *cmd, int new_state)
|
||||
{
|
||||
ktime_t now;
|
||||
uint64_t nowc;
|
||||
int sz, dir;
|
||||
struct scst_lat_stat_entry *prev_stat = NULL, *new_stat;
|
||||
unsigned long flags;
|
||||
|
||||
sBUG_ON(new_state >= SCST_CMD_STATE_COUNT);
|
||||
|
||||
now = ktime_get();
|
||||
#ifdef SCST_MEASURE_CLOCK_CYCLES
|
||||
nowc = rdtsc();
|
||||
#else
|
||||
nowc = 0;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* expected_transfer_len_full is only available once the state
|
||||
* SCST_CMD_STATE_INIT has been reached.
|
||||
*/
|
||||
if (new_state == SCST_CMD_STATE_INIT_WAIT) {
|
||||
cmd->init_wait_time = now;
|
||||
#ifdef SCST_MEASURE_CLOCK_CYCLES
|
||||
cmd->init_wait_tsc = nowc;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
WARN_ON_ONCE(!cmd->sess);
|
||||
|
||||
if (!cmd->sess->lat_stats)
|
||||
return;
|
||||
|
||||
/* To do: subtract size of T10 PI data from data length */
|
||||
sz = ilog2(roundup_pow_of_two(cmd->expected_transfer_len_full)) -
|
||||
SCST_STATS_LOG2_SZ_OFFSET;
|
||||
if (sz < 0)
|
||||
sz = 0;
|
||||
else if (sz >= SCST_STATS_MAX_LOG2_SZ)
|
||||
sz = SCST_STATS_MAX_LOG2_SZ - 1;
|
||||
dir = cmd->expected_data_direction & 3;
|
||||
if (new_state != SCST_CMD_STATE_INIT_WAIT)
|
||||
prev_stat = &cmd->sess->lat_stats->ls[sz][dir][cmd->state];
|
||||
new_stat = &cmd->sess->lat_stats->ls[sz][dir][new_state];
|
||||
|
||||
spin_lock_irqsave(&cmd->sess->lat_stats_lock, flags);
|
||||
if (new_state == SCST_CMD_STATE_INIT)
|
||||
__scst_update_latency_stats(cmd, NULL, prev_stat,
|
||||
cmd->init_wait_time,
|
||||
cmd->init_wait_tsc);
|
||||
__scst_update_latency_stats(cmd, prev_stat, new_stat, now, nowc);
|
||||
spin_unlock_irqrestore(&cmd->sess->lat_stats_lock, flags);
|
||||
}
|
||||
|
||||
@@ -177,6 +177,9 @@ unsigned int scst_max_dev_cmd_mem;
|
||||
int scst_forcibly_close_sessions;
|
||||
int scst_auto_cm_assignment = true;
|
||||
|
||||
spinlock_t scst_measure_latency_lock;
|
||||
atomic_t scst_measure_latency;
|
||||
|
||||
module_param_named(scst_threads, scst_threads, int, S_IRUGO);
|
||||
MODULE_PARM_DESC(scst_threads, "SCSI target threads count");
|
||||
|
||||
@@ -715,11 +718,15 @@ static const char *const scst_cmd_state_name[] = {
|
||||
[SCST_CMD_STATE_MODE_SELECT_CHECKS] = "MODE_SELECT_CHECKS",
|
||||
[SCST_CMD_STATE_DEV_DONE] = "DEV_DONE",
|
||||
[SCST_CMD_STATE_PRE_XMIT_RESP] = "PRE_XMIT_RESP",
|
||||
[SCST_CMD_STATE_PRE_XMIT_RESP1] = "PRE_XMIT_RESP1",
|
||||
[SCST_CMD_STATE_CSW2] = "CSW2",
|
||||
[SCST_CMD_STATE_PRE_XMIT_RESP2] = "PRE_XMIT_RESP2",
|
||||
[SCST_CMD_STATE_XMIT_RESP] = "XMIT_RESP",
|
||||
[SCST_CMD_STATE_FINISHED] = "FINISHED",
|
||||
[SCST_CMD_STATE_FINISHED_INTERNAL] = "FINISHED_INTERNAL",
|
||||
[SCST_CMD_STATE_INIT_WAIT] = "INIT_WAIT",
|
||||
[SCST_CMD_STATE_INIT] = "INIT",
|
||||
[SCST_CMD_STATE_CSW1] = "CSW1",
|
||||
[SCST_CMD_STATE_PREPROCESSING_DONE_CALLED] = "PREP_DONE_CALLED",
|
||||
[SCST_CMD_STATE_DATA_WAIT] = "DATA_WAIT",
|
||||
[SCST_CMD_STATE_EXEC_CHECK_BLOCKING] = "EXEC_CHECK_BLOCKING",
|
||||
@@ -2544,6 +2551,7 @@ static int __init init_scst(void)
|
||||
mutex_init(&scst_cmd_threads_mutex);
|
||||
INIT_LIST_HEAD(&scst_cmd_threads_list);
|
||||
cpumask_setall(&default_cpu_mask);
|
||||
spin_lock_init(&scst_measure_latency_lock);
|
||||
|
||||
scst_init_threads(&scst_main_cmd_threads);
|
||||
|
||||
|
||||
@@ -87,8 +87,15 @@ extern unsigned long scst_trace_flag;
|
||||
/* Set if new commands initialization is suspended for a while */
|
||||
#define SCST_FLAG_SUSPENDED 1
|
||||
|
||||
static inline void scst_set_cmd_state(struct scst_cmd *cmd, enum scst_cmd_state new_state)
|
||||
extern spinlock_t scst_measure_latency_lock;
|
||||
extern atomic_t scst_measure_latency;
|
||||
void scst_update_latency_stats(struct scst_cmd *cmd, int new_state);
|
||||
|
||||
static inline void scst_set_cmd_state(struct scst_cmd *cmd,
|
||||
enum scst_cmd_state new_state)
|
||||
{
|
||||
if (unlikely(atomic_read(&scst_measure_latency)))
|
||||
scst_update_latency_stats(cmd, new_state);
|
||||
cmd->state = new_state;
|
||||
}
|
||||
|
||||
|
||||
@@ -4313,6 +4313,132 @@ void scst_tgt_dev_sysfs_del(struct scst_tgt_dev *tgt_dev)
|
||||
** Sessions subdirectory implementation
|
||||
**/
|
||||
|
||||
static u64 calc_stddev(u64 sumsq, u64 sum, u32 count)
|
||||
{
|
||||
return int_sqrt64((sumsq - sum * sum / count) / count);
|
||||
}
|
||||
|
||||
static ssize_t scst_sess_latency_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
struct scst_session *sess =
|
||||
container_of(kobj->parent, struct scst_session, sess_kobj);
|
||||
int res = 0, i, j, k;
|
||||
long sz;
|
||||
struct scst_lat_stat_entry *d;
|
||||
uint64_t avg, stddev;
|
||||
#ifdef SCST_MEASURE_CLOCK_CYCLES
|
||||
uint64_t min, max, sumc = 0, sumsqc = 0;
|
||||
#else
|
||||
uint64_t sum = 0, sumsq = 0;
|
||||
#endif
|
||||
unsigned count = 0, numst = 0;
|
||||
char state_name[32];
|
||||
|
||||
switch (attr->attr.name[0]) {
|
||||
case 'n': j = SCST_DATA_NONE & 3; break;
|
||||
case 'r': j = SCST_DATA_READ; break;
|
||||
case 'w': j = SCST_DATA_WRITE; break;
|
||||
case 'b': j = SCST_DATA_BIDI; break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
|
||||
res = kstrtol(attr->attr.name + 1, 0, &sz);
|
||||
#else
|
||||
res = strict_strtol(attr->attr.name + 1, 0, &sz);
|
||||
#endif
|
||||
if (WARN_ON(res < 0))
|
||||
goto out;
|
||||
i = ilog2(sz) - SCST_STATS_LOG2_SZ_OFFSET;
|
||||
if (WARN_ON(i < 0 || i >= SCST_STATS_MAX_LOG2_SZ)) {
|
||||
res = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
res += scnprintf(buf + res, PAGE_SIZE - res,
|
||||
"state count min max avg stddev\n");
|
||||
|
||||
spin_lock_irq(&sess->lat_stats_lock);
|
||||
for (k = 0; k < SCST_CMD_STATE_COUNT; k++) {
|
||||
struct scst_lat_stats *lat_stats = sess->lat_stats;
|
||||
|
||||
d = &lat_stats->ls[i][j][k];
|
||||
if (!lat_stats || d->count == 0 || res >= PAGE_SIZE)
|
||||
continue;
|
||||
scst_get_cmd_state_name(state_name, sizeof(state_name),
|
||||
k);
|
||||
avg = d->sum / d->count;
|
||||
stddev = calc_stddev(d->sumsq, d->sum, d->count);
|
||||
res += scnprintf(buf + res, PAGE_SIZE - res,
|
||||
"%s %d %lld.%01lld %lld.%01lld %lld.%01lld %lld.%01lld us\n",
|
||||
state_name, d->count,
|
||||
d->min / 10, d->min % 10,
|
||||
d->max / 10, d->max % 10,
|
||||
avg / 10, avg % 10,
|
||||
stddev / 10, stddev % 10);
|
||||
#ifdef SCST_MEASURE_CLOCK_CYCLES
|
||||
min = d->minc * 10000 / (tsc_khz / 100);
|
||||
max = d->maxc * 10000 / (tsc_khz / 100);
|
||||
avg = d->sumc * 10000 / (d->count * 1ull * tsc_khz / 100);
|
||||
stddev = calc_stddev(d->sumsqc, d->sumc, d->count)
|
||||
* 1000000 / tsc_khz;
|
||||
res += scnprintf(buf + res, PAGE_SIZE - res,
|
||||
"%s %d %lld.%01lld %lld.%01lld %lld.%01lld %lld.%01lld cc -> us\n",
|
||||
state_name, d->count,
|
||||
min / 10, min % 10,
|
||||
max / 10, max % 10,
|
||||
avg / 10, avg % 10,
|
||||
stddev / 10, stddev % 10);
|
||||
sumc += d->sumc;
|
||||
sumsqc += d->sumsqc;
|
||||
#else
|
||||
sum += d->sum;
|
||||
sumsq += d->sumsq;
|
||||
#endif
|
||||
count += d->count;
|
||||
numst++;
|
||||
}
|
||||
spin_unlock_irq(&sess->lat_stats_lock);
|
||||
|
||||
if (count != 0) {
|
||||
#ifdef SCST_MEASURE_CLOCK_CYCLES
|
||||
avg = numst * sumc / (count * 1ull * tsc_khz / 1000000);
|
||||
stddev = calc_stddev(sumsqc, sumc, count) * numst *
|
||||
1000000 / tsc_khz;
|
||||
res += scnprintf(buf + res, PAGE_SIZE - res,
|
||||
"total %d - - %lld.%01lld %lld.%01lld cc -> us\n",
|
||||
count / numst, avg / 10, avg % 10, stddev / 10,
|
||||
stddev % 10);
|
||||
#else
|
||||
avg = numst * sum / count;
|
||||
stddev = calc_stddev(sumsq, sum, count) * numst;
|
||||
res += scnprintf(buf + res, PAGE_SIZE - res,
|
||||
"total %d - - %lld.%01lld %lld.%01lld us\n",
|
||||
count / numst, avg / 10, avg % 10, stddev / 10,
|
||||
stddev % 10);
|
||||
#endif
|
||||
}
|
||||
|
||||
out:
|
||||
return res;
|
||||
}
|
||||
|
||||
static ssize_t scst_sess_latency_store(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct scst_session *sess =
|
||||
container_of(kobj->parent, struct scst_session, sess_kobj);
|
||||
|
||||
spin_lock_irq(&sess->lat_stats_lock);
|
||||
BUILD_BUG_ON(sizeof(*sess->lat_stats) != sizeof(struct scst_lat_stats));
|
||||
memset(sess->lat_stats, 0, sizeof(*sess->lat_stats));
|
||||
spin_unlock_irq(&sess->lat_stats_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t scst_sess_sysfs_commands_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
@@ -4679,6 +4805,74 @@ static struct kobj_type scst_session_ktype = {
|
||||
.default_attrs = scst_session_attrs,
|
||||
};
|
||||
|
||||
#define SCST_LAT_ATTRS(size) \
|
||||
&sess_lat_attr_n##size.attr, \
|
||||
&sess_lat_attr_r##size.attr, \
|
||||
&sess_lat_attr_w##size.attr, \
|
||||
&sess_lat_attr_b##size.attr
|
||||
|
||||
#define SCST_LAT_ATTR(size) \
|
||||
static struct kobj_attribute sess_lat_attr_n##size = \
|
||||
__ATTR(n##size, S_IRUGO | S_IWUSR, scst_sess_latency_show,\
|
||||
scst_sess_latency_store); \
|
||||
static struct kobj_attribute sess_lat_attr_r##size = \
|
||||
__ATTR(r##size, S_IRUGO | S_IWUSR, scst_sess_latency_show, \
|
||||
scst_sess_latency_store); \
|
||||
static struct kobj_attribute sess_lat_attr_w##size = \
|
||||
__ATTR(w##size, S_IRUGO | S_IWUSR, scst_sess_latency_show, \
|
||||
scst_sess_latency_store); \
|
||||
static struct kobj_attribute sess_lat_attr_b##size = \
|
||||
__ATTR(b##size, S_IRUGO | S_IWUSR, scst_sess_latency_show, \
|
||||
scst_sess_latency_store);
|
||||
SCST_LAT_ATTR(512);
|
||||
SCST_LAT_ATTR(1024);
|
||||
SCST_LAT_ATTR(2048);
|
||||
SCST_LAT_ATTR(4096);
|
||||
SCST_LAT_ATTR(8192);
|
||||
SCST_LAT_ATTR(16384);
|
||||
SCST_LAT_ATTR(32768);
|
||||
SCST_LAT_ATTR(65536);
|
||||
SCST_LAT_ATTR(131072);
|
||||
SCST_LAT_ATTR(262144);
|
||||
SCST_LAT_ATTR(524288);
|
||||
|
||||
static const struct attribute *scst_sess_lat_attr[] = {
|
||||
SCST_LAT_ATTRS(512),
|
||||
SCST_LAT_ATTRS(1024),
|
||||
SCST_LAT_ATTRS(2048),
|
||||
SCST_LAT_ATTRS(4096),
|
||||
SCST_LAT_ATTRS(8192),
|
||||
SCST_LAT_ATTRS(16384),
|
||||
SCST_LAT_ATTRS(32768),
|
||||
SCST_LAT_ATTRS(65536),
|
||||
SCST_LAT_ATTRS(131072),
|
||||
SCST_LAT_ATTRS(262144),
|
||||
SCST_LAT_ATTRS(524288),
|
||||
NULL,
|
||||
};
|
||||
|
||||
static int scst_create_latency_attrs(struct scst_session *sess)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = -ENOMEM;
|
||||
sess->lat_kobj = kobject_create_and_add("latency", &sess->sess_kobj);
|
||||
if (sess->lat_kobj == NULL)
|
||||
goto out;
|
||||
|
||||
res = sysfs_create_files(sess->lat_kobj, scst_sess_lat_attr);
|
||||
if (res < 0)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
return res;
|
||||
}
|
||||
|
||||
static void scst_remove_latency_attrs(struct scst_session *sess)
|
||||
{
|
||||
kobject_del(sess->lat_kobj);
|
||||
}
|
||||
|
||||
static int scst_create_sess_luns_link(struct scst_session *sess)
|
||||
{
|
||||
int res;
|
||||
@@ -4764,6 +4958,10 @@ int scst_sess_sysfs_create(struct scst_session *sess)
|
||||
goto out_del;
|
||||
}
|
||||
|
||||
res = scst_create_latency_attrs(sess);
|
||||
if (res != 0)
|
||||
goto out_del;
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
@@ -4794,6 +4992,7 @@ void scst_sess_sysfs_del(struct scst_session *sess)
|
||||
|
||||
sess->sess_kobj_release_cmpl = &c;
|
||||
|
||||
scst_remove_latency_attrs(sess);
|
||||
kobject_del(&sess->sess_kobj);
|
||||
|
||||
SCST_KOBJECT_PUT_AND_WAIT(&sess->sess_kobj, "session", &c,
|
||||
@@ -6689,6 +6888,112 @@ static const struct attribute *scst_device_groups_attrs[] = {
|
||||
|
||||
static struct kobject scst_sysfs_root_kobj;
|
||||
|
||||
static ssize_t scst_measure_latency_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", atomic_read(&scst_measure_latency));
|
||||
}
|
||||
|
||||
static void scst_free_lat_stats_mem(void)
|
||||
{
|
||||
struct scst_tgt_template *tt;
|
||||
struct scst_tgt *tgt;
|
||||
struct scst_session *sess;
|
||||
|
||||
lockdep_assert_held(&scst_mutex);
|
||||
|
||||
list_for_each_entry(tt, &scst_template_list, scst_template_list_entry) {
|
||||
list_for_each_entry(tgt, &tt->tgt_list, tgt_list_entry) {
|
||||
list_for_each_entry(sess, &tgt->sess_list,
|
||||
sess_list_entry) {
|
||||
vfree(sess->lat_stats);
|
||||
sess->lat_stats = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int scst_alloc_lat_stats_mem(void)
|
||||
{
|
||||
struct scst_tgt_template *tt;
|
||||
struct scst_tgt *tgt;
|
||||
struct scst_session *sess;
|
||||
|
||||
lockdep_assert_held(&scst_mutex);
|
||||
|
||||
list_for_each_entry(tt, &scst_template_list, scst_template_list_entry) {
|
||||
list_for_each_entry(tgt, &tt->tgt_list, tgt_list_entry) {
|
||||
list_for_each_entry(sess, &tgt->sess_list,
|
||||
sess_list_entry) {
|
||||
sess->lat_stats =
|
||||
vzalloc(sizeof(*sess->lat_stats));
|
||||
if (!sess->lat_stats) {
|
||||
scst_free_lat_stats_mem();
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t scst_measure_latency_store(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
bool prev_val;
|
||||
long val;
|
||||
int res;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
|
||||
res = kstrtol(buf, 0, &val);
|
||||
#else
|
||||
res = strict_strtol(buf, 0, &val);
|
||||
#endif
|
||||
if (res < 0)
|
||||
goto out;
|
||||
|
||||
val = !!val;
|
||||
|
||||
res = scst_suspend_activity(true);
|
||||
if (res)
|
||||
goto out;
|
||||
res = mutex_lock_interruptible(&scst_mutex);
|
||||
if (res)
|
||||
goto out_resume;
|
||||
|
||||
spin_lock(&scst_measure_latency_lock);
|
||||
prev_val = atomic_read(&scst_measure_latency);
|
||||
atomic_set(&scst_measure_latency, val);
|
||||
spin_unlock(&scst_measure_latency_lock);
|
||||
|
||||
if (prev_val != val) {
|
||||
if (val) {
|
||||
res = scst_alloc_lat_stats_mem();
|
||||
if (res)
|
||||
goto out_unlock;
|
||||
} else {
|
||||
scst_free_lat_stats_mem();
|
||||
}
|
||||
}
|
||||
|
||||
res = count;
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&scst_mutex);
|
||||
|
||||
out_resume:
|
||||
scst_resume_activity();
|
||||
|
||||
out:
|
||||
return res;
|
||||
}
|
||||
|
||||
static struct kobj_attribute scst_measure_latency_attr =
|
||||
__ATTR(measure_latency, S_IRUGO | S_IWUSR,
|
||||
scst_measure_latency_show,
|
||||
scst_measure_latency_store);
|
||||
|
||||
static ssize_t scst_threads_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
@@ -7238,6 +7543,7 @@ static struct kobj_attribute scst_last_sysfs_mgmt_res_attr =
|
||||
scst_last_sysfs_mgmt_res_show, NULL);
|
||||
|
||||
static struct attribute *scst_sysfs_root_default_attrs[] = {
|
||||
&scst_measure_latency_attr.attr,
|
||||
&scst_threads_attr.attr,
|
||||
&scst_setup_id_attr.attr,
|
||||
&scst_max_tasklet_cmd_attr.attr,
|
||||
|
||||
@@ -872,6 +872,9 @@ set_state:
|
||||
if (unlikely(rc < 0))
|
||||
goto out;
|
||||
|
||||
if (cmd->state == SCST_CMD_STATE_PARSE)
|
||||
scst_set_cmd_state(cmd, SCST_CMD_STATE_CSW1);
|
||||
|
||||
active:
|
||||
/* Here cmd must not be in any cmd list, no locks */
|
||||
switch (pref_context) {
|
||||
@@ -4383,6 +4386,9 @@ static int scst_dev_done(struct scst_cmd *cmd)
|
||||
#endif
|
||||
|
||||
out:
|
||||
if (cmd->state == SCST_CMD_STATE_PRE_XMIT_RESP1)
|
||||
scst_set_cmd_state(cmd, SCST_CMD_STATE_CSW2);
|
||||
|
||||
TRACE_EXIT_HRES(res);
|
||||
return res;
|
||||
}
|
||||
@@ -5430,6 +5436,7 @@ void scst_process_active_cmd(struct scst_cmd *cmd, bool atomic)
|
||||
|
||||
do {
|
||||
switch (cmd->state) {
|
||||
case SCST_CMD_STATE_CSW1:
|
||||
case SCST_CMD_STATE_PARSE:
|
||||
res = scst_parse_cmd(cmd);
|
||||
break;
|
||||
@@ -5507,6 +5514,7 @@ void scst_process_active_cmd(struct scst_cmd *cmd, bool atomic)
|
||||
res = scst_dev_done(cmd);
|
||||
break;
|
||||
|
||||
case SCST_CMD_STATE_CSW2:
|
||||
case SCST_CMD_STATE_PRE_XMIT_RESP1:
|
||||
res = scst_pre_xmit_response1(cmd);
|
||||
EXTRACHECKS_BUG_ON(res == SCST_CMD_STATE_RES_NEED_THREAD);
|
||||
|
||||
Reference in New Issue
Block a user