From: Andrey Grafin <andrey.gra...@acronis.com>

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.gra...@acronis.com>
Acked-by: Andrei Vagin <ava...@virtuozzo.com>

(cherry picked from dcec5ba86d70452dc79f30261c0e217ff2c253bd)
Signed-off-by: Andrey Zhadchenko <andrey.zhadche...@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 844d076..07ce36e 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 ssize_t target_stat_tgt_port_hs_in_cmds_show(struct 
config_item *item,
        &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 6894778..17f506c 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 f5afe9f..243e2a0 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 1d1f3a9..82a87ce 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 {
-- 
1.8.3.1

_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel

Reply via email to