* squares (for multi-valued counter samples only). This allows
* external computation of standard deviation, but involves a 64-bit
* multiply per counter increment.
+ *
+ * LPROCFS_CNTR_HISTOGRAM indicates that the counter should track a
+ * exponential histogram.
*/
enum lprocfs_counter_config {
LPROCFS_CNTR_EXTERNALLOCK = 0x0001,
LPROCFS_CNTR_AVGMINMAX = 0x0002,
LPROCFS_CNTR_STDDEV = 0x0004,
+ LPROCFS_CNTR_HISTOGRAM = 0x0008,
/* counter unit type */
LPROCFS_TYPE_REQS = 0x0000, /* default if config = 0 */
LPROCFS_TYPE_BYTES_FULL = LPROCFS_TYPE_BYTES |
LPROCFS_CNTR_AVGMINMAX |
LPROCFS_CNTR_STDDEV,
+ LPROCFS_TYPE_BYTES_FULL_HISTOGRAM = LPROCFS_TYPE_BYTES_FULL |
+ LPROCFS_CNTR_HISTOGRAM,
};
#define LC_MIN_INIT ((~(__u64)0) >> 1)
enum lprocfs_counter_config lc_config;
const char *lc_name; /* must be static */
const char *lc_units; /* must be static */
+ struct obd_histogram *lc_hist;
};
struct lprocfs_counter {
#define JOBSTATS_SESSION "session"
typedef void (*cntr_init_callback)(struct lprocfs_stats *stats,
- unsigned int offset);
-
+ unsigned int offset,
+ enum lprocfs_counter_config cntr_umask);
struct obd_job_stats {
struct cfs_hash *ojs_hash; /* hash of jobids */
struct list_head ojs_list; /* list of job_stat structs */
if (stats->nid_stats == NULL)
RETURN(-ENOMEM);
- mdt_stats_counter_init(stats->nid_stats, 0);
+ mdt_stats_counter_init(stats->nid_stats, 0, LPROCFS_CNTR_HISTOGRAM);
rc = lprocfs_register_stats(stats->nid_proc, "stats", stats->nid_stats);
if (rc != 0) {
};
void mdt_counter_incr(struct ptlrpc_request *req, int opcode, long amount);
-void mdt_stats_counter_init(struct lprocfs_stats *stats, unsigned int offset);
+void mdt_stats_counter_init(struct lprocfs_stats *stats, unsigned int offset,
+ enum lprocfs_counter_config cntr_umask);
int mdt_tunables_init(struct mdt_device *mdt, const char *name);
void mdt_tunables_fini(struct mdt_device *mdt);
[LPROC_MDT_FALLOCATE] = "fallocate",
};
-void mdt_stats_counter_init(struct lprocfs_stats *stats, unsigned int offset)
+void mdt_stats_counter_init(struct lprocfs_stats *stats, unsigned int offset,
+ enum lprocfs_counter_config cntr_umask)
{
int array_size = ARRAY_SIZE(mdt_stats);
int oidx; /* obd_md_stats index */
if (midx == LPROC_MDT_IO_READ_BYTES ||
midx == LPROC_MDT_IO_WRITE_BYTES)
lprocfs_counter_init(stats, oidx,
- LPROCFS_TYPE_BYTES_FULL,
+ LPROCFS_TYPE_BYTES_FULL_HISTOGRAM &
+ (~cntr_umask),
mdt_stats[midx]);
else
- lprocfs_counter_init(stats, oidx, LPROCFS_TYPE_LATENCY,
+ lprocfs_counter_init(stats, oidx,
+ LPROCFS_TYPE_LATENCY &
+ (~cntr_umask),
mdt_stats[midx]);
}
}
return rc;
/* add additional MDT md_stats after the default ones */
- mdt_stats_counter_init(obd->obd_md_stats, LPROC_MD_LAST_OPC);
+ mdt_stats_counter_init(obd->obd_md_stats, LPROC_MD_LAST_OPC,
+ LPROCFS_CNTR_HISTOGRAM);
rc = lprocfs_job_stats_init(obd, ARRAY_SIZE(mdt_stats),
mdt_stats_counter_init);
#ifdef CONFIG_PROC_FS
void lprocfs_counter_add(struct lprocfs_stats *stats, int idx, long amount)
{
- struct lprocfs_counter *percpu_cntr;
- struct lprocfs_counter_header *header;
- int smp_id;
- unsigned long flags = 0;
+ struct lprocfs_counter *percpu_cntr;
+ struct lprocfs_counter_header *header;
+ int smp_id;
+ unsigned long flags = 0;
+ struct obd_histogram *hist;
if (stats == NULL)
return;
if (amount > percpu_cntr->lc_max)
percpu_cntr->lc_max = amount;
}
+ /* no counter in interrupt has historgram for now */
+ hist = stats->ls_cnt_header[idx].lc_hist;
+ if (hist != NULL) {
+ unsigned int val = 0;
+
+ if (likely(amount != 0))
+ val = min(fls(amount - 1), OBD_HIST_MAX - 1);
+ spin_lock(&hist->oh_lock);
+ hist->oh_buckets[val]++;
+ spin_unlock(&hist->oh_lock);
+ }
+
lprocfs_stats_unlock(stats, LPROCFS_GET_SMP_ID, &flags);
}
EXPORT_SYMBOL(lprocfs_counter_add);
return NULL;
}
- jobs->ojs_cntr_init_fn(job->js_stats, 0);
+ jobs->ojs_cntr_init_fn(job->js_stats, 0, 0);
memcpy(job->js_jobid, jobid, sizeof(job->js_jobid));
job->js_time_init = ktime_get();
int i, joblen = 0;
if (v == SEQ_START_TOKEN) {
- seq_printf(p, "job_stats:\n");
+ seq_puts(p, "job_stats:\n");
return 0;
}
s = job->js_stats;
for (i = 0; i < s->ls_num; i++) {
+ struct obd_histogram *hist;
+
cntr_header = &s->ls_cnt_header[i];
lprocfs_stats_collect(s, i, &ret);
ret.lc_count ? ret.lc_sumsquare : 0);
}
- seq_printf(p, " }\n");
-
+ /* show obd_histogram */
+ hist = s->ls_cnt_header[i].lc_hist;
+ if (hist != NULL) {
+ bool first = true;
+ int j;
+
+ seq_puts(p, ", hist: { ");
+ for (j = 0; j < ARRAY_SIZE(hist->oh_buckets); j++) {
+ unsigned long val = hist->oh_buckets[j];
+
+ if (val == 0)
+ continue;
+ if (first)
+ first = false;
+ else
+ seq_puts(p, ", ");
+
+ if (j < 10)
+ seq_printf(p, "%lu: %lu", BIT(j), val);
+ else if (j < 20)
+ seq_printf(p, "%luK: %lu", BIT(j - 10),
+ val);
+ else if (j < 30)
+ seq_printf(p, "%luM: %lu", BIT(j - 20),
+ val);
+ else
+ seq_printf(p, "%luG: %lu", BIT(j - 30),
+ val);
+ }
+ seq_puts(p, " }");
+ }
+ seq_puts(p, " }\n");
}
return 0;
for (i = 0; i < num_entry; i++)
if (stats->ls_percpu[i])
LIBCFS_FREE(stats->ls_percpu[i], percpusize);
- if (stats->ls_cnt_header)
+
+ if (stats->ls_cnt_header) {
+ for (i = 0; i < stats->ls_num; i++)
+ if (stats->ls_cnt_header[i].lc_hist != NULL)
+ CFS_FREE_PTR(stats->ls_cnt_header[i].lc_hist);
CFS_FREE_PTR_ARRAY(stats->ls_cnt_header, stats->ls_num);
+ }
+
LIBCFS_FREE(stats, offsetof(typeof(*stats), ls_percpu[num_entry]));
}
EXPORT_SYMBOL(lprocfs_free_stats);
void lprocfs_clear_stats(struct lprocfs_stats *stats)
{
struct lprocfs_counter *percpu_cntr;
- int i;
- int j;
+ int i, j;
unsigned int num_entry;
unsigned long flags = 0;
num_entry = lprocfs_stats_lock(stats, LPROCFS_GET_NUM_CPU, &flags);
+ /* clear histogram if exists */
+ for (j = 0; j < stats->ls_num; j++) {
+ struct obd_histogram *hist = stats->ls_cnt_header[j].lc_hist;
+
+ if (hist != NULL)
+ lprocfs_oh_clear(hist);
+ }
+
for (i = 0; i < num_entry; i++) {
if (!stats->ls_percpu[i])
continue;
header->lc_name = name;
header->lc_units = units;
+ if (config & LPROCFS_CNTR_HISTOGRAM) {
+ CFS_ALLOC_PTR(stats->ls_cnt_header[index].lc_hist);
+ if (stats->ls_cnt_header[index].lc_hist == NULL)
+ CERROR("LprocFS: Failed to allocate histogram:[%d]%s/%s\n",
+ index, name, units);
+ else
+ spin_lock_init(&stats->ls_cnt_header[index].lc_hist->oh_lock);
+ }
num_cpu = lprocfs_stats_lock(stats, LPROCFS_GET_NUM_CPU, &flags);
for (i = 0; i < num_cpu; ++i) {
if (!stats->ls_percpu[i])
*
* param[in] stats statistics counters
*/
-void ofd_stats_counter_init(struct lprocfs_stats *stats, unsigned int offset)
+void ofd_stats_counter_init(struct lprocfs_stats *stats, unsigned int offset,
+ enum lprocfs_counter_config cntr_umask)
{
LASSERT(stats && stats->ls_num >= LPROC_OFD_STATS_LAST);
lprocfs_counter_init(stats, LPROC_OFD_STATS_READ_BYTES,
- LPROCFS_TYPE_BYTES_FULL, "read_bytes");
+ LPROCFS_TYPE_BYTES_FULL_HISTOGRAM & (~cntr_umask),
+ "read_bytes");
lprocfs_counter_init(stats, LPROC_OFD_STATS_WRITE_BYTES,
- LPROCFS_TYPE_BYTES_FULL, "write_bytes");
+ LPROCFS_TYPE_BYTES_FULL_HISTOGRAM & (~cntr_umask),
+ "write_bytes");
lprocfs_counter_init(stats, LPROC_OFD_STATS_READ,
- LPROCFS_TYPE_LATENCY, "read");
+ LPROCFS_TYPE_LATENCY & (~cntr_umask), "read");
lprocfs_counter_init(stats, LPROC_OFD_STATS_WRITE,
- LPROCFS_TYPE_LATENCY, "write");
+ LPROCFS_TYPE_LATENCY & (~cntr_umask), "write");
lprocfs_counter_init(stats, LPROC_OFD_STATS_GETATTR,
- LPROCFS_TYPE_LATENCY, "getattr");
+ LPROCFS_TYPE_LATENCY & (~cntr_umask), "getattr");
lprocfs_counter_init(stats, LPROC_OFD_STATS_SETATTR,
- LPROCFS_TYPE_LATENCY, "setattr");
+ LPROCFS_TYPE_LATENCY & (~cntr_umask), "setattr");
lprocfs_counter_init(stats, LPROC_OFD_STATS_PUNCH,
- LPROCFS_TYPE_LATENCY, "punch");
+ LPROCFS_TYPE_LATENCY & (~cntr_umask), "punch");
lprocfs_counter_init(stats, LPROC_OFD_STATS_SYNC,
- LPROCFS_TYPE_LATENCY, "sync");
+ LPROCFS_TYPE_LATENCY & (~cntr_umask), "sync");
lprocfs_counter_init(stats, LPROC_OFD_STATS_DESTROY,
- LPROCFS_TYPE_LATENCY, "destroy");
+ LPROCFS_TYPE_LATENCY & (~cntr_umask), "destroy");
lprocfs_counter_init(stats, LPROC_OFD_STATS_CREATE,
- LPROCFS_TYPE_LATENCY, "create");
+ LPROCFS_TYPE_LATENCY & (~cntr_umask), "create");
lprocfs_counter_init(stats, LPROC_OFD_STATS_STATFS,
- LPROCFS_TYPE_LATENCY, "statfs");
+ LPROCFS_TYPE_LATENCY & (~cntr_umask), "statfs");
lprocfs_counter_init(stats, LPROC_OFD_STATS_GET_INFO,
- LPROCFS_TYPE_LATENCY, "get_info");
+ LPROCFS_TYPE_LATENCY & (~cntr_umask), "get_info");
lprocfs_counter_init(stats, LPROC_OFD_STATS_SET_INFO,
- LPROCFS_TYPE_LATENCY, "set_info");
+ LPROCFS_TYPE_LATENCY & (~cntr_umask), "set_info");
lprocfs_counter_init(stats, LPROC_OFD_STATS_QUOTACTL,
- LPROCFS_TYPE_LATENCY, "quotactl");
+ LPROCFS_TYPE_LATENCY & (~cntr_umask), "quotactl");
lprocfs_counter_init(stats, LPROC_OFD_STATS_PREALLOC,
- LPROCFS_TYPE_LATENCY, "prealloc");
+ LPROCFS_TYPE_LATENCY & (~cntr_umask), "prealloc");
}
LPROC_SEQ_FOPS(lprocfs_nid_stats_clear);
GOTO(obd_free_stats, rc);
}
- ofd_stats_counter_init(obd->obd_stats, 0);
+ ofd_stats_counter_init(obd->obd_stats, 0, LPROCFS_CNTR_HISTOGRAM);
rc = lprocfs_job_stats_init(obd, LPROC_OFD_STATS_LAST,
ofd_stats_counter_init);
/* lproc_ofd.c */
int ofd_tunables_init(struct ofd_device *ofd);
#ifdef CONFIG_PROC_FS
-void ofd_stats_counter_init(struct lprocfs_stats *stats, unsigned int offset);
+void ofd_stats_counter_init(struct lprocfs_stats *stats, unsigned int offset,
+ enum lprocfs_counter_config cntr_umask);
#else
-static inline void ofd_stats_counter_init(struct lprocfs_stats *stats,
- unsigned int offset) {}
+static inline void
+ofd_stats_counter_init(struct lprocfs_stats *stats,
+ unsigned int offset,
+ enum lprocfs_counter_config cntr_umask) {}
#endif
/* ofd_objects.c */
/* see comment on LPROC_OFD_STATS_WRITE_BYTES usage above */
ofd_counter_incr(exp, LPROC_OFD_STATS_READ_BYTES, jobid, nob);
ofd_counter_incr(exp, LPROC_OFD_STATS_READ, jobid,
- ktime_us_delta(ktime_get(), kstart));
+ ktime_us_delta(ktime_get(), kstart));
rc = ofd_commitrw_read(env, ofd, fid, objcount,
npages, lnb);
if (!stats->nid_stats)
RETURN(-ENOMEM);
- ofd_stats_counter_init(stats->nid_stats, 0);
+ ofd_stats_counter_init(stats->nid_stats, 0, LPROCFS_CNTR_HISTOGRAM);
rc = lprocfs_register_stats(stats->nid_proc, "stats", stats->nid_stats);
if (rc != 0) {