+struct lprocfs_stats *lprocfs_alloc_stats(unsigned int num)
+{
+ struct lprocfs_stats *stats;
+ struct lprocfs_percpu *percpu;
+ unsigned int percpusize;
+ unsigned int i;
+
+ if (num == 0)
+ return NULL;
+
+ OBD_ALLOC(stats, offsetof(typeof(*stats), ls_percpu[num_online_cpus()]));
+ if (stats == NULL)
+ return NULL;
+
+ percpusize = L1_CACHE_ALIGN(offsetof(typeof(*percpu), lp_cntr[num]));
+ stats->ls_percpu_size = num_online_cpus() * percpusize;
+ OBD_ALLOC(stats->ls_percpu[0], stats->ls_percpu_size);
+ if (stats->ls_percpu[0] == NULL) {
+ OBD_FREE(stats, offsetof(typeof(*stats),
+ ls_percpu[num_online_cpus()]));
+ return NULL;
+ }
+
+ stats->ls_num = num;
+ for (i = 1; i < num_online_cpus(); i++)
+ stats->ls_percpu[i] = (void *)(stats->ls_percpu[i - 1]) +
+ percpusize;
+
+ return stats;
+}
+
+void lprocfs_free_stats(struct lprocfs_stats *stats)
+{
+ if (stats->ls_num == 0)
+ return;
+
+ OBD_FREE(stats->ls_percpu[0], stats->ls_percpu_size);
+ OBD_FREE(stats, offsetof(typeof(*stats), ls_percpu[num_online_cpus()]));
+}
+
+/* Reset counter under lock */
+int lprocfs_counter_write(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+ /* not supported */
+ return 0;
+}
+
+static void *lprocfs_stats_seq_start(struct seq_file *p, loff_t *pos)
+{
+ struct lprocfs_stats *stats = p->private;
+ /* return 1st cpu location */
+ return (*pos >= stats->ls_num) ? NULL :
+ &(stats->ls_percpu[0]->lp_cntr[*pos]);
+}
+
+static void lprocfs_stats_seq_stop(struct seq_file *p, void *v)
+{
+}
+
+static void *lprocfs_stats_seq_next(struct seq_file *p, void *v, loff_t *pos)
+{
+ struct lprocfs_stats *stats = p->private;
+ ++*pos;
+ return (*pos >= stats->ls_num) ? NULL :
+ &(stats->ls_percpu[0]->lp_cntr[*pos]);
+}
+
+/* seq file export of one lprocfs counter */
+static int lprocfs_stats_seq_show(struct seq_file *p, void *v)
+{
+ struct lprocfs_stats *stats = p->private;
+ struct lprocfs_counter *cntr = v;
+ struct lprocfs_counter t, ret = { .lc_min = ~(__u64)0 };
+ int i, idx, rc;
+
+ if (cntr == &(stats->ls_percpu[0])->lp_cntr[0]) {
+ struct timeval now;
+ do_gettimeofday(&now);
+ rc = seq_printf(p, "%-25s %lu.%lu secs.usecs\n",
+ "snapshot_time", now.tv_sec, now.tv_usec);
+ if (rc < 0)
+ return rc;
+ }
+ idx = cntr - &(stats->ls_percpu[0])->lp_cntr[0];
+
+ for (i = 0; i < num_online_cpus(); i++) {
+ struct lprocfs_counter *percpu_cntr =
+ &(stats->ls_percpu[i])->lp_cntr[idx];
+ int centry;
+
+ do {
+ centry = atomic_read(&percpu_cntr->lc_cntl.la_entry);
+ t.lc_count = percpu_cntr->lc_count;
+ t.lc_sum = percpu_cntr->lc_sum;
+ t.lc_min = percpu_cntr->lc_min;
+ t.lc_max = percpu_cntr->lc_max;
+ t.lc_sumsquare = percpu_cntr->lc_sumsquare;
+ } while (centry != atomic_read(&percpu_cntr->lc_cntl.la_entry) &&
+ centry != atomic_read(&percpu_cntr->lc_cntl.la_exit));
+ ret.lc_count += t.lc_count;
+ ret.lc_sum += t.lc_sum;
+ if (t.lc_min < ret.lc_min)
+ ret.lc_min = t.lc_min;
+ if (t.lc_max > ret.lc_max)
+ ret.lc_max = t.lc_max;
+ ret.lc_sumsquare += t.lc_sumsquare;
+ }
+
+ rc = seq_printf(p, "%-25s "LPU64" samples [%s]", cntr->lc_name,
+ ret.lc_count, cntr->lc_units);
+ if (rc < 0)
+ goto out;
+
+ if ((cntr->lc_config & LPROCFS_CNTR_AVGMINMAX) && (ret.lc_count > 0)) {
+ rc = seq_printf(p, " "LPU64" "LPU64" "LPU64,
+ ret.lc_min, ret.lc_max, ret.lc_sum);
+ if (rc < 0)
+ goto out;
+ if (cntr->lc_config & LPROCFS_CNTR_STDDEV)
+ rc = seq_printf(p, " "LPU64, ret.lc_sumsquare);
+ if (rc < 0)
+ goto out;
+ }
+ rc = seq_printf(p, "\n");
+ out:
+ return (rc < 0) ? rc : 0;
+}
+
+struct seq_operations lprocfs_stats_seq_sops = {
+ start: lprocfs_stats_seq_start,
+ stop: lprocfs_stats_seq_stop,
+ next: lprocfs_stats_seq_next,
+ show: lprocfs_stats_seq_show,
+};
+
+static int lprocfs_stats_seq_open(struct inode *inode, struct file *file)
+{
+ struct proc_dir_entry *dp = PDE(inode);
+ struct seq_file *seq;
+ int rc;
+
+ rc = seq_open(file, &lprocfs_stats_seq_sops);
+ if (rc)
+ return rc;
+ seq = file->private_data;
+ seq->private = dp->data;
+ return 0;
+}
+
+struct file_operations lprocfs_stats_seq_fops = {
+ open: lprocfs_stats_seq_open,
+ read: seq_read,
+ llseek: seq_lseek,
+ release: seq_release,
+};
+
+int lprocfs_register_stats(struct proc_dir_entry *root, const char *name,
+ struct lprocfs_stats *stats)
+{
+ struct proc_dir_entry *entry;
+ LASSERT(root != NULL);
+
+ entry = create_proc_entry(name, 0444, root);
+ if (entry == NULL)
+ return -ENOMEM;
+ entry->proc_fops = &lprocfs_stats_seq_fops;
+ entry->data = (void *)stats;
+ entry->write_proc = lprocfs_counter_write;
+ return 0;
+}
+
+void lprocfs_counter_init(struct lprocfs_stats *stats, int index,
+ unsigned conf, const char *name, const char *units)
+{
+ struct lprocfs_counter *c;
+ int i;
+
+ LASSERT(stats != NULL);
+ for (i = 0; i < num_online_cpus(); i++) {
+ c = &(stats->ls_percpu[i]->lp_cntr[index]);
+ c->lc_config = conf;
+ c->lc_min = ~(__u64)0;
+ c->lc_name = name;
+ c->lc_units = units;
+ }
+}
+EXPORT_SYMBOL(lprocfs_counter_init);
+
+#define LPROCFS_OBD_OP_INIT(base, stats, op) \
+do { \
+ unsigned int coffset = base + OBD_COUNTER_OFFSET(op); \
+ LASSERT(coffset < stats->ls_num); \
+ lprocfs_counter_init(stats, coffset, 0, #op, "reqs"); \
+} while (0)
+
+int lprocfs_alloc_obd_stats(struct obd_device *obd, unsigned num_private_stats)
+{
+ struct lprocfs_stats *stats;
+ unsigned int num_stats;
+ int rc, i;
+
+ LASSERT(obd->obd_stats == NULL);
+ LASSERT(obd->obd_proc_entry != NULL);
+ LASSERT(obd->obd_cntr_base == 0);
+
+ num_stats = 1 + OBD_COUNTER_OFFSET(notify) +
+ num_private_stats;
+ stats = lprocfs_alloc_stats(num_stats);
+ if (stats == NULL)
+ return -ENOMEM;
+
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, iocontrol);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, get_info);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, set_info);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, attach);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, detach);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, setup);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, postsetup);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, precleanup);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, cleanup);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, postrecov);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, connect);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, disconnect);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, statfs);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, packmd);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, unpackmd);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, preallocate);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, create);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, destroy);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, setattr);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, getattr);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, getattr_async);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, brw);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, brw_async);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, prep_async_page);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, queue_async_io);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, set_async_flags);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, queue_sync_io);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, trigger_sync_io);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, teardown_async_page);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, punch);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, sync);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, migrate);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, copy);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, iterate);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, preprw);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, commitrw);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, enqueue);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, match);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, change_cbdata);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, cancel);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, cancel_unused);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, san_preprw);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, init_export);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, destroy_export);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, lock_contains);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, llog_init);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, llog_finish);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, pin);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, unpin);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, invalidate_import);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, notify);
+
+ for (i = num_private_stats; i < num_stats; i++) {
+ /* If this LBUGs, it is likely that an obd
+ * operation was added to struct obd_ops in
+ * <linux/obd.h>, and that the corresponding line item
+ * LPROCFS_OBD_OP_INIT(.., .., opname)
+ * is missing from the list above. */
+ if (stats->ls_percpu[0]->lp_cntr[i].lc_name == NULL) {
+ CERROR("Missing obd_stat initializer obd_op "
+ "operation at offset %d. Aborting.\n",
+ i - num_private_stats);
+ LBUG();
+ }
+ }
+ rc = lprocfs_register_stats(obd->obd_proc_entry, "stats", stats);
+ if (rc < 0) {
+ lprocfs_free_stats(stats);
+ } else {
+ obd->obd_stats = stats;
+ obd->obd_cntr_base = num_private_stats;
+ }
+ return rc;
+}
+
+void lprocfs_free_obd_stats(struct obd_device *obd)
+{
+ struct lprocfs_stats *stats = obd->obd_stats;
+
+ if (stats != NULL) {
+ obd->obd_stats = NULL;
+ lprocfs_free_stats(stats);
+ }
+}
+
+int lprocfs_write_helper(const char *buffer, unsigned long count,
+ int *val)
+{
+ char kernbuf[20], *end;
+
+ if (count > (sizeof(kernbuf) - 1))
+ return -EINVAL;
+
+ if (copy_from_user(kernbuf, buffer, count))
+ return -EFAULT;
+
+ kernbuf[count] = '\0';
+
+ *val = simple_strtol(kernbuf, &end, 0);
+ if (kernbuf == end)
+ return -EINVAL;
+
+ return 0;
+}
+
+int lprocfs_write_u64_helper(const char *buffer, unsigned long count,
+ __u64 *val)
+{
+ char kernbuf[22], *end;
+
+ if (count > (sizeof(kernbuf) - 1))
+ return -EINVAL;
+
+ if (copy_from_user(kernbuf, buffer, count))
+ return -EFAULT;
+
+ kernbuf[count] = '\0';
+
+ *val = simple_strtoull(kernbuf, &end, 0);
+ if (kernbuf == end)
+ return -EINVAL;
+
+ return 0;
+}
+
+int lprocfs_obd_seq_create(struct obd_device *dev, char *name, mode_t mode,
+ struct file_operations *seq_fops, void *data)
+{
+ struct proc_dir_entry *entry;
+ ENTRY;
+
+ entry = create_proc_entry(name, mode, dev->obd_proc_entry);
+ if (entry == NULL)
+ RETURN(-ENOMEM);
+ entry->proc_fops = seq_fops;
+ entry->data = data;
+
+ RETURN(0);
+}
+EXPORT_SYMBOL(lprocfs_obd_seq_create);
+
+void lprocfs_oh_tally(struct obd_histogram *oh, unsigned int value)
+{
+ unsigned long flags;
+
+ if (value >= OBD_HIST_MAX)
+ value = OBD_HIST_MAX - 1;
+
+ spin_lock_irqsave(&oh->oh_lock, flags);
+ oh->oh_buckets[value]++;
+ spin_unlock_irqrestore(&oh->oh_lock, flags);
+}
+EXPORT_SYMBOL(lprocfs_oh_tally);
+
+void lprocfs_oh_tally_log2(struct obd_histogram *oh, unsigned int value)
+{
+ unsigned int val;
+
+ for (val = 0; ((1 << val) < value) && (val <= OBD_HIST_MAX); val++)
+ ;
+
+ lprocfs_oh_tally(oh, val);
+}
+EXPORT_SYMBOL(lprocfs_oh_tally_log2);
+
+unsigned long lprocfs_oh_sum(struct obd_histogram *oh)
+{
+ unsigned long ret = 0;
+ int i;
+
+ for (i = 0; i < OBD_HIST_MAX; i++)
+ ret += oh->oh_buckets[i];
+ return ret;
+}
+EXPORT_SYMBOL(lprocfs_oh_sum);
+
+void lprocfs_oh_clear(struct obd_histogram *oh)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&oh->oh_lock, flags);
+ memset(oh->oh_buckets, 0, sizeof(oh->oh_buckets));
+ spin_unlock_irqrestore(&oh->oh_lock, flags);
+}
+EXPORT_SYMBOL(lprocfs_oh_clear);
+