+#include <lustre_osc.h>
+#include <cl_object.h>
+#include "mdc_internal.h"
+
+static ssize_t active_show(struct kobject *kobj, struct attribute *attr,
+ char *buf)
+{
+ struct obd_device *obd = container_of(kobj, struct obd_device,
+ obd_kset.kobj);
+ struct obd_import *imp;
+ ssize_t len;
+
+ with_imp_locked(obd, imp, len)
+ len = sprintf(buf, "%d\n", !imp->imp_deactive);
+ return len;
+}
+
+static ssize_t active_store(struct kobject *kobj, struct attribute *attr,
+ const char *buffer, size_t count)
+{
+ struct obd_device *obd = container_of(kobj, struct obd_device,
+ obd_kset.kobj);
+ bool val;
+ int rc;
+
+ rc = kstrtobool(buffer, &val);
+ if (rc)
+ return rc;
+
+ /* opposite senses */
+ if (obd->u.cli.cl_import->imp_deactive == val)
+ rc = ptlrpc_set_import_active(obd->u.cli.cl_import, val);
+ else
+ CDEBUG(D_CONFIG, "activate %u: ignoring repeat request\n",
+ val);
+
+ return count;
+}
+LUSTRE_RW_ATTR(active);
+
+static ssize_t max_rpcs_in_flight_show(struct kobject *kobj,
+ struct attribute *attr,
+ char *buf)
+{
+ struct obd_device *obd = container_of(kobj, struct obd_device,
+ obd_kset.kobj);
+ ssize_t len;
+ u32 max;
+
+ max = obd_get_max_rpcs_in_flight(&obd->u.cli);
+ len = sprintf(buf, "%u\n", max);
+
+ return len;
+}
+
+static ssize_t max_rpcs_in_flight_store(struct kobject *kobj,
+ struct attribute *attr,
+ const char *buffer,
+ size_t count)
+{
+ struct obd_device *obd = container_of(kobj, struct obd_device,
+ obd_kset.kobj);
+ unsigned int val;
+ int rc;
+
+ rc = kstrtouint(buffer, 10, &val);
+ if (rc)
+ return rc;
+
+ rc = obd_set_max_rpcs_in_flight(&obd->u.cli, val);
+ if (rc)
+ count = rc;
+
+ return count;
+}
+LUSTRE_RW_ATTR(max_rpcs_in_flight);
+
+static ssize_t max_mod_rpcs_in_flight_show(struct kobject *kobj,
+ struct attribute *attr,
+ char *buf)
+{
+ struct obd_device *obd = container_of(kobj, struct obd_device,
+ obd_kset.kobj);
+ u16 max;
+
+ max = obd_get_max_mod_rpcs_in_flight(&obd->u.cli);
+ return sprintf(buf, "%hu\n", max);
+}
+
+static ssize_t max_mod_rpcs_in_flight_store(struct kobject *kobj,
+ struct attribute *attr,
+ const char *buffer,
+ size_t count)
+{
+ struct obd_device *obd = container_of(kobj, struct obd_device,
+ obd_kset.kobj);
+ u16 val;
+ int rc;
+
+ rc = kstrtou16(buffer, 10, &val);
+ if (rc)
+ return rc;
+
+ rc = obd_set_max_mod_rpcs_in_flight(&obd->u.cli, val);
+ if (rc)
+ count = rc;
+
+ return count;
+}
+LUSTRE_RW_ATTR(max_mod_rpcs_in_flight);
+
+static int mdc_max_dirty_mb_seq_show(struct seq_file *m, void *v)
+{
+ struct obd_device *obd = m->private;
+ struct client_obd *cli = &obd->u.cli;
+
+ seq_printf(m, "%lu\n", PAGES_TO_MiB(cli->cl_dirty_max_pages));
+ return 0;
+}
+
+static ssize_t mdc_max_dirty_mb_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct seq_file *sfl = file->private_data;
+ struct obd_device *obd = sfl->private;
+ struct client_obd *cli = &obd->u.cli;
+ char kernbuf[22] = "";
+ u64 pages_number;
+ int rc;
+
+ if (count >= sizeof(kernbuf))
+ return -EINVAL;
+
+ if (copy_from_user(kernbuf, buffer, count))
+ return -EFAULT;
+ kernbuf[count] = 0;
+
+ rc = sysfs_memparse(kernbuf, count, &pages_number, "MiB");
+ if (rc < 0)
+ return rc;
+
+ /* MB -> pages */
+ pages_number = round_up(pages_number, 1024 * 1024) >> PAGE_SHIFT;
+ if (pages_number <= 0 ||
+ pages_number >= MiB_TO_PAGES(OSC_MAX_DIRTY_MB_MAX) ||
+ pages_number > cfs_totalram_pages() / 4) /* 1/4 of RAM */
+ return -ERANGE;
+
+ spin_lock(&cli->cl_loi_list_lock);
+ cli->cl_dirty_max_pages = pages_number;
+ osc_wake_cache_waiters(cli);
+ spin_unlock(&cli->cl_loi_list_lock);
+
+ return count;
+}
+LPROC_SEQ_FOPS(mdc_max_dirty_mb);
+
+static ssize_t contention_seconds_show(struct kobject *kobj,
+ struct attribute *attr,
+ char *buf)
+{
+ struct obd_device *obd = container_of(kobj, struct obd_device,
+ obd_kset.kobj);
+ struct osc_device *od = obd2osc_dev(obd);
+
+ return sprintf(buf, "%lld\n", od->od_contention_time);
+}
+
+static ssize_t contention_seconds_store(struct kobject *kobj,
+ struct attribute *attr,
+ const char *buffer,
+ size_t count)
+{
+ struct obd_device *obd = container_of(kobj, struct obd_device,
+ obd_kset.kobj);
+ struct osc_device *od = obd2osc_dev(obd);
+ time64_t val;
+ int rc;
+
+ rc = kstrtoll(buffer, 0, &val);
+ if (rc)
+ return rc;
+
+ od->od_contention_time = val;
+
+ return count;
+}
+LUSTRE_RW_ATTR(contention_seconds);
+
+LUSTRE_ATTR(mds_conn_uuid, 0444, conn_uuid_show, NULL);
+LUSTRE_RO_ATTR(conn_uuid);
+
+LUSTRE_RW_ATTR(ping);
+
+static int mdc_cached_mb_seq_show(struct seq_file *m, void *v)
+{
+ struct obd_device *obd = m->private;
+ struct client_obd *cli = &obd->u.cli;
+ int shift = 20 - PAGE_SHIFT;
+
+ seq_printf(m, "used_mb: %ld\n"
+ "busy_cnt: %ld\n"
+ "reclaim: %llu\n",
+ (atomic_long_read(&cli->cl_lru_in_list) +
+ atomic_long_read(&cli->cl_lru_busy)) >> shift,
+ atomic_long_read(&cli->cl_lru_busy),
+ cli->cl_lru_reclaim);
+
+ return 0;
+}
+
+/* shrink the number of caching pages to a specific number */
+static ssize_t
+mdc_cached_mb_seq_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct seq_file *sfl = file->private_data;
+ struct obd_device *obd = sfl->private;
+ struct client_obd *cli = &obd->u.cli;
+ u64 pages_number;
+ const char *tmp;
+ long rc;
+ char kernbuf[128];
+
+ if (count >= sizeof(kernbuf))
+ return -EINVAL;
+
+ if (copy_from_user(kernbuf, buffer, count))
+ return -EFAULT;
+ kernbuf[count] = 0;
+
+ tmp = lprocfs_find_named_value(kernbuf, "used_mb:", &count);
+ rc = sysfs_memparse(tmp, count, &pages_number, "MiB");
+ if (rc < 0)
+ return rc;
+
+ pages_number >>= PAGE_SHIFT;
+
+ rc = atomic_long_read(&cli->cl_lru_in_list) - pages_number;
+ if (rc > 0) {
+ struct lu_env *env;
+ __u16 refcheck;
+
+ env = cl_env_get(&refcheck);
+ if (!IS_ERR(env)) {
+ (void)osc_lru_shrink(env, cli, rc, true);
+ cl_env_put(env, &refcheck);
+ }
+ }
+
+ return count;
+}
+LPROC_SEQ_FOPS(mdc_cached_mb);
+
+static int mdc_unstable_stats_seq_show(struct seq_file *m, void *v)
+{
+ struct obd_device *obd = m->private;
+ struct client_obd *cli = &obd->u.cli;
+ long pages;
+ int mb;
+
+ pages = atomic_long_read(&cli->cl_unstable_count);
+ mb = (pages * PAGE_SIZE) >> 20;
+
+ seq_printf(m, "unstable_pages: %20ld\n"
+ "unstable_mb: %10d\n", pages, mb);
+ return 0;
+}
+LPROC_SEQ_FOPS_RO(mdc_unstable_stats);
+
+static ssize_t mdc_rpc_stats_seq_write(struct file *file,
+ const char __user *buf,
+ size_t len, loff_t *off)
+{
+ struct seq_file *seq = file->private_data;
+ struct obd_device *obd = seq->private;
+ struct client_obd *cli = &obd->u.cli;
+
+ lprocfs_oh_clear(&cli->cl_mod_rpcs_hist);
+
+ lprocfs_oh_clear(&cli->cl_read_rpc_hist);
+ lprocfs_oh_clear(&cli->cl_write_rpc_hist);
+ lprocfs_oh_clear(&cli->cl_read_page_hist);
+ lprocfs_oh_clear(&cli->cl_write_page_hist);
+ lprocfs_oh_clear(&cli->cl_read_offset_hist);
+ lprocfs_oh_clear(&cli->cl_write_offset_hist);
+
+ return len;
+}
+
+static int mdc_rpc_stats_seq_show(struct seq_file *seq, void *v)
+{
+ struct obd_device *obd = seq->private;
+ struct client_obd *cli = &obd->u.cli;
+ unsigned long read_tot = 0, write_tot = 0, read_cum, write_cum;
+ int i;
+
+ obd_mod_rpc_stats_seq_show(cli, seq);
+
+ spin_lock(&cli->cl_loi_list_lock);
+
+ seq_printf(seq, "\nread RPCs in flight: %d\n",
+ cli->cl_r_in_flight);
+ seq_printf(seq, "write RPCs in flight: %d\n",
+ cli->cl_w_in_flight);
+ seq_printf(seq, "pending write pages: %d\n",
+ atomic_read(&cli->cl_pending_w_pages));
+ seq_printf(seq, "pending read pages: %d\n",
+ atomic_read(&cli->cl_pending_r_pages));
+
+ seq_printf(seq, "\n\t\t\tread\t\t\twrite\n");
+ seq_printf(seq, "pages per rpc rpcs %% cum %% |");
+ seq_printf(seq, " rpcs %% cum %%\n");
+
+ read_tot = lprocfs_oh_sum(&cli->cl_read_page_hist);
+ write_tot = lprocfs_oh_sum(&cli->cl_write_page_hist);
+
+ read_cum = 0;
+ write_cum = 0;
+ for (i = 0; i < OBD_HIST_MAX; i++) {
+ unsigned long r = cli->cl_read_page_hist.oh_buckets[i];
+ unsigned long w = cli->cl_write_page_hist.oh_buckets[i];
+
+ read_cum += r;
+ write_cum += w;
+ seq_printf(seq, "%d:\t\t%10lu %3u %3u | %10lu %3u %3u\n",
+ 1 << i, r, pct(r, read_tot),
+ pct(read_cum, read_tot), w,
+ pct(w, write_tot),
+ pct(write_cum, write_tot));
+ if (read_cum == read_tot && write_cum == write_tot)
+ break;
+ }
+
+ seq_printf(seq, "\n\t\t\tread\t\t\twrite\n");
+ seq_printf(seq, "rpcs in flight rpcs %% cum %% |");
+ seq_printf(seq, " rpcs %% cum %%\n");
+
+ read_tot = lprocfs_oh_sum(&cli->cl_read_rpc_hist);
+ write_tot = lprocfs_oh_sum(&cli->cl_write_rpc_hist);
+
+ read_cum = 0;
+ write_cum = 0;
+ for (i = 0; i < OBD_HIST_MAX; i++) {
+ unsigned long r = cli->cl_read_rpc_hist.oh_buckets[i];
+ unsigned long w = cli->cl_write_rpc_hist.oh_buckets[i];