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