[Devel] [PATCH RHEL8 COMMIT] target: add histogram for LUN statistics
Konstantin Khorenko
khorenko at virtuozzo.com
Fri Apr 30 14:33:45 MSK 2021
The commit is pushed to "branch-rh8-4.18.0-240.1.1.vz8.5.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh8-4.18.0-240.1.1.vz8.5.24
------>
commit 67fe4f5410633d1d2c3e8c5b3537a5fe82ce4b2a
Author: Andrey Grafin <Andrey.Grafin at acronis.com>
Date: Fri Apr 30 14:33:44 2021 +0300
target: add histogram for LUN statistics
This patch adds histogram statistics to scsi_ports_stats.
Histogram can be obtained and configured via config_fs.
Histogram measurement unit is usec.
Histogram usage:
1. Configure histogram '| 1 ms | 10 ms | 15 ms | largest values |'
`echo "1000 10000 15000" > target/iscsi/iqn.2003-01.org.linux-iscsi.localhost.x8664\:sn.fdee138936b9/tpgt_1/lun/lun_3/statistics/scsi_tgt_port/write_hist`
2. Obtain histogram stats
`cat /target/iscsi/iqn.2003-01.org.linux-iscsi.localhost.x8664\:sn.fdee138936b9/tpgt_1/lun/lun_3/statistics/scsi_tgt_port/write_hist`
3. Stop histogram stats
`echo "" > /target/iscsi/iqn.2003-01.org.linux-iscsi.localhost.x8664\:sn.fdee138936b9/tpgt_1/lun/lun_3/statistics/scsi_tgt_port/write_hist`
Signed-off-by: Andrey Grafin <Andrey.Grafin at acronis.com>
Acked-by: Andrei Vagin <avagin at virtuozzo.com>
(cherry picked from vz7 commit dcec5ba86d70 ("target: add histogram for LUN
statistics")
Signed-off-by: Andrey Zhadchenko <andrey.zhadchenko at virtuozzo.com>
---
drivers/target/target_core_stat.c | 206 +++++++++++++++++++++++++++++++++
drivers/target/target_core_tpg.c | 19 +++
drivers/target/target_core_transport.c | 25 ++++
include/target/target_core_base.h | 16 +++
4 files changed, 266 insertions(+)
diff --git a/drivers/target/target_core_stat.c b/drivers/target/target_core_stat.c
index 844d076b5c9f..07ce36e83f91 100644
--- a/drivers/target/target_core_stat.c
+++ b/drivers/target/target_core_stat.c
@@ -33,6 +33,7 @@
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/configfs.h>
+#include <linux/ctype.h>
#include <target/target_core_base.h>
#include <target/target_core_backend.h>
@@ -537,6 +538,208 @@ static struct se_lun *to_stat_tgt_port(struct config_item *item)
return container_of(pgrps, struct se_lun, port_stat_grps);
}
+#define DEV_STAT_TGT_PORT_SHOW_HIST(_name) \
+static ssize_t target_stat_tgt_port_##_name##_show( \
+ struct config_item *item, char *page) \
+{ \
+ ssize_t size = -ENODEV; \
+ struct se_device *dev; \
+ struct se_lun *lun = to_stat_port(item); \
+ \
+ rcu_read_lock(); \
+ dev = rcu_dereference(lun->lun_se_dev); \
+ if (dev) { \
+ size = snprintf_histogram(page, PAGE_SIZE, \
+ rcu_dereference(lun->lun_stats._name)); \
+ } \
+ rcu_read_unlock(); \
+ return size; \
+}
+
+#define DEV_STAT_TGT_PORT_STORE_HIST(_name) \
+static ssize_t target_stat_tgt_port_##_name##_store( \
+ struct config_item *item, const char *page, size_t size) \
+{ \
+ struct se_device *dev; \
+ struct se_lun *lun = to_stat_port(item); \
+ struct scsi_port_stats_hist *old, *new; \
+ ssize_t ret; \
+ \
+ new = kzalloc(sizeof(*new), GFP_KERNEL); \
+ if (!new) \
+ return -ENOMEM; \
+ \
+ ret = read_histogram_items(page, \
+ size, new->items, TCM_SE_PORT_STATS_HIST_MAX - 1); \
+ \
+ if (ret < 0) \
+ goto err; \
+ \
+ if (ret == 0) { \
+ kfree(new); \
+ new = NULL; \
+ } else { \
+ new->items[ret] = U64_MAX; \
+ new->count = ret + 1; \
+ } \
+ \
+ rcu_read_lock(); \
+ dev = rcu_dereference(lun->lun_se_dev); \
+ \
+ if (!dev) { \
+ rcu_read_unlock(); \
+ ret = -ENODEV; \
+ goto err; \
+ } \
+ \
+ old = rcu_dereference(lun->lun_stats._name); \
+ rcu_assign_pointer(lun->lun_stats._name, new); \
+ \
+ rcu_read_unlock(); \
+ \
+ if (old) \
+ kfree_rcu(old, rcu_head); \
+ \
+ return size; \
+ \
+err: \
+ if (new) \
+ kfree(new); \
+ return ret; \
+}
+
+static void scsi_port_stats_hist_observe_bsearch(
+ struct scsi_port_stats_hist *hist, u64 val)
+{
+ size_t start = 0, end = hist->count - 1;
+
+ while (start < end) {
+ size_t mid = start + (end - start) / 2;
+
+ if (val < hist->items[mid])
+ end = mid;
+ else
+ start = mid + 1;
+ }
+
+ atomic64_inc(&hist->counters[start]);
+}
+
+void scsi_port_stats_hist_observe(
+ struct scsi_port_stats_hist *hist, u64 val)
+{
+ if (!hist)
+ return;
+
+ scsi_port_stats_hist_observe_bsearch(hist, val);
+}
+
+static ssize_t find_token(const char *page, size_t size,
+ size_t offset, size_t *token_len)
+{
+ size_t i;
+ ssize_t pos = -1;
+ size_t len = 0;
+
+ BUG_ON(offset > size);
+
+ for (i = offset; i < size; ++i) {
+ if (isspace(page[i])) {
+ if (len)
+ break;
+ } else if (!len++) {
+ pos = i;
+ }
+ }
+
+ if (pos > -1)
+ *token_len = len;
+
+ return pos;
+}
+
+static ssize_t read_histogram_items(const char *page, size_t size,
+ u64 *items, u8 items_max)
+{
+ u8 total = 0;
+ size_t token_len = 0;
+ ssize_t pos;
+
+ if (size == 0)
+ return 0;
+
+ pos = find_token(page, size, 0, &token_len);
+ if (pos == -1)
+ return 0;
+
+ while (pos != -1) {
+ int ret;
+ char buf[64];
+ u64 item;
+
+ if (token_len >= sizeof(buf))
+ return -EINVAL;
+
+ if (total == items_max) {
+ pr_err("items count can't be greater than %d: %d",
+ items_max, -EPERM);
+ return -EINVAL;
+ }
+
+ memcpy(buf, page + pos, token_len);
+ buf[token_len] = 0;
+
+ ret = kstrtou64(buf, 10, &item);
+ if (ret < 0) {
+ pr_err("kstrtou64() failed for an item '%s': %d",
+ buf, ret);
+ return ret;
+ }
+
+ if ((item <= 0) || (total && item <= items[total - 1])) {
+ pr_err("items must be positive, unique and sorted: %d",
+ -EINVAL);
+ return -EINVAL;
+ }
+
+ items[total++] = item;
+ pos = find_token(page, size, pos + token_len, &token_len);
+ }
+
+ return total;
+}
+
+static ssize_t snprintf_histogram(char *page, size_t size,
+ struct scsi_port_stats_hist *hist)
+{
+ ssize_t ret = 0;
+ u8 i;
+
+ if (!hist)
+ return 0;
+
+ for (i = 0; i < hist->count - 1; ++i)
+ ret += snprintf(page + ret, PAGE_SIZE - ret,
+ "%llu ", (u64)atomic64_read(&hist->counters[i]));
+
+ ret += snprintf(page + ret, PAGE_SIZE - ret,
+ "%llu\n", (u64)atomic64_read(&hist->counters[i]));
+
+ return ret;
+}
+
+DEV_STAT_TGT_PORT_STORE_HIST(read_hist);
+DEV_STAT_TGT_PORT_SHOW_HIST(read_hist);
+CONFIGFS_ATTR(target_stat_tgt_port_, read_hist);
+
+DEV_STAT_TGT_PORT_STORE_HIST(write_hist);
+DEV_STAT_TGT_PORT_SHOW_HIST(write_hist);
+CONFIGFS_ATTR(target_stat_tgt_port_, write_hist);
+
+DEV_STAT_TGT_PORT_STORE_HIST(sync_hist);
+DEV_STAT_TGT_PORT_SHOW_HIST(sync_hist);
+CONFIGFS_ATTR(target_stat_tgt_port_, sync_hist);
+
static ssize_t target_stat_tgt_port_inst_show(struct config_item *item,
char *page)
{
@@ -754,6 +957,9 @@ static struct configfs_attribute *target_stat_scsi_tgt_port_attrs[] = {
&target_stat_tgt_port_attr_bidi_errors,
&target_stat_tgt_port_attr_aborts,
&target_stat_tgt_port_attr_queue_cmds,
+ &target_stat_tgt_port_attr_read_hist,
+ &target_stat_tgt_port_attr_write_hist,
+ &target_stat_tgt_port_attr_sync_hist,
NULL,
};
diff --git a/drivers/target/target_core_tpg.c b/drivers/target/target_core_tpg.c
index 689477895576..17f506c82d94 100644
--- a/drivers/target/target_core_tpg.c
+++ b/drivers/target/target_core_tpg.c
@@ -634,6 +634,7 @@ void core_tpg_remove_lun(
* reference to se_device->dev_group.
*/
struct se_device *dev = rcu_dereference_raw(lun->lun_se_dev);
+ struct scsi_port_stats_hist *write_hist, *read_hist, *sync_hist;
lun->lun_shutdown = true;
@@ -662,5 +663,23 @@ void core_tpg_remove_lun(
lun->lun_shutdown = false;
mutex_unlock(&tpg->tpg_lun_mutex);
+ write_hist = rcu_dereference(lun->lun_stats.write_hist);
+ rcu_assign_pointer(lun->lun_stats.write_hist, NULL);
+
+ read_hist = rcu_dereference(lun->lun_stats.read_hist);
+ rcu_assign_pointer(lun->lun_stats.read_hist, NULL);
+
+ sync_hist = rcu_dereference(lun->lun_stats.sync_hist);
+ rcu_assign_pointer(lun->lun_stats.sync_hist, NULL);
+
+ if (write_hist)
+ kfree_rcu(write_hist, rcu_head);
+
+ if (read_hist)
+ kfree_rcu(read_hist, rcu_head);
+
+ if (sync_hist)
+ kfree_rcu(sync_hist, rcu_head);
+
percpu_ref_exit(&lun->lun_ref);
}
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index f5afe9f34d86..243e2a057b7c 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -888,6 +888,9 @@ void target_complete_cmd(struct se_cmd *cmd, u8 scsi_status)
cmd->transport_state |= (CMD_T_COMPLETE | CMD_T_ACTIVE);
spin_unlock_irqrestore(&cmd->t_state_lock, flags);
+ if (success)
+ cmd->finished_exec_at = ktime_get_ns() / NSEC_PER_USEC;
+
INIT_WORK(&cmd->work, success ? target_complete_ok_work :
target_complete_failure_work);
if (cmd->se_cmd_flags & SCF_USE_CPUID)
@@ -1991,6 +1994,7 @@ void __target_execute_cmd(struct se_cmd *cmd, bool do_checks)
}
}
+ cmd->started_exec_at = ktime_get_ns() / NSEC_PER_USEC;
ret = cmd->execute_cmd(cmd);
if (!ret)
return;
@@ -2373,6 +2377,15 @@ static void target_complete_ok_work(struct work_struct *work)
}
queue_rsp:
+ if (cmd->t_task_cdb[0] == SYNCHRONIZE_CACHE ||
+ cmd->t_task_cdb[0] == SYNCHRONIZE_CACHE_16) {
+ rcu_read_lock();
+ scsi_port_stats_hist_observe(
+ rcu_dereference(cmd->se_lun->lun_stats.sync_hist),
+ cmd->finished_exec_at - cmd->started_exec_at);
+ rcu_read_unlock();
+ }
+
switch (cmd->data_direction) {
case DMA_FROM_DEVICE:
/*
@@ -2394,6 +2407,12 @@ static void target_complete_ok_work(struct work_struct *work)
atomic_long_inc(&cmd->se_lun->lun_stats.read_cmds);
+ rcu_read_lock();
+ scsi_port_stats_hist_observe(
+ rcu_dereference(cmd->se_lun->lun_stats.read_hist),
+ cmd->finished_exec_at - cmd->started_exec_at);
+ rcu_read_unlock();
+
/*
* Perform READ_STRIP of PI using software emulation when
* backend had PI enabled, if the transport will not be
@@ -2423,6 +2442,12 @@ static void target_complete_ok_work(struct work_struct *work)
atomic_long_inc(&cmd->se_lun->lun_stats.bidi_cmds) :
atomic_long_inc(&cmd->se_lun->lun_stats.write_cmds);
+ rcu_read_lock();
+ scsi_port_stats_hist_observe(
+ rcu_dereference(cmd->se_lun->lun_stats.write_hist),
+ cmd->finished_exec_at - cmd->started_exec_at);
+ rcu_read_unlock();
+
/*
* Check if we need to send READ payload for BIDI-COMMAND
*/
diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h
index 1d1f3a91c5d0..82a87ce349ab 100644
--- a/include/target/target_core_base.h
+++ b/include/target/target_core_base.h
@@ -541,6 +541,8 @@ struct se_cmd {
sense_reason_t pi_err;
sector_t bad_sector;
int cpuid;
+ u64 started_exec_at;
+ u64 finished_exec_at;
};
struct se_ua {
@@ -711,6 +713,17 @@ struct se_port_stat_grps {
struct config_group scsi_transport_group;
};
+#define TCM_SE_PORT_STATS_HIST_MAX 40
+
+struct scsi_port_stats_hist {
+ u64 items[TCM_SE_PORT_STATS_HIST_MAX];
+ atomic64_t counters[TCM_SE_PORT_STATS_HIST_MAX];
+ u8 count;
+ struct rcu_head rcu_head;
+};
+
+void scsi_port_stats_hist_observe(struct scsi_port_stats_hist *hist, u64 val);
+
struct scsi_port_stats {
atomic_long_t cmd_pdus;
atomic_long_t tx_data_octets;
@@ -723,6 +736,9 @@ struct scsi_port_stats {
atomic_long_t bidi_errors;
atomic_long_t queue_cmds;
atomic_long_t aborts;
+ struct scsi_port_stats_hist __rcu *read_hist;
+ struct scsi_port_stats_hist __rcu *write_hist;
+ struct scsi_port_stats_hist __rcu *sync_hist;
};
struct se_lun {
More information about the Devel
mailing list