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:
Bart Van Assche
2018-09-27 04:09:27 +00:00
parent 8e454788bd
commit 5f1cf8cf1d
7 changed files with 579 additions and 1 deletions
+50
View File
@@ -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)
+69
View File
@@ -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;
+130
View File
@@ -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);
}
+8
View File
@@ -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);
+8 -1
View File
@@ -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;
}
+306
View File
@@ -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,
+8
View File
@@ -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);