1/*
2 * GPL HEADER START
3 *
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 only,
8 * as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * General Public License version 2 for more details (a copy is included
14 * in the LICENSE file that accompanied this code).
15 *
16 * You should have received a copy of the GNU General Public License
17 * version 2 along with this program; If not, see
18 * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
19 *
20 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
21 * CA 95054 USA or visit www.sun.com if you need additional information or
22 * have any questions.
23 *
24 * GPL HEADER END
25 */
26/*
27 * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
28 * Use is subject to license terms.
29 *
30 * Copyright (c) 2011, 2012, Intel Corporation.
31 */
32/*
33 * This file is part of Lustre, http://www.lustre.org/
34 * Lustre is a trademark of Sun Microsystems, Inc.
35 */
36#define DEBUG_SUBSYSTEM S_CLASS
37
38#include <asm/statfs.h>
39#include "../include/obd_cksum.h"
40#include "../include/obd_class.h"
41#include "../include/lprocfs_status.h"
42#include <linux/seq_file.h>
43#include "osc_internal.h"
44
45static int osc_active_seq_show(struct seq_file *m, void *v)
46{
47	struct obd_device *dev = m->private;
48	int rc;
49
50	LPROCFS_CLIMP_CHECK(dev);
51	rc = seq_printf(m, "%d\n", !dev->u.cli.cl_import->imp_deactive);
52	LPROCFS_CLIMP_EXIT(dev);
53	return rc;
54}
55
56static ssize_t osc_active_seq_write(struct file *file, const char *buffer,
57				    size_t count, loff_t *off)
58{
59	struct obd_device *dev = ((struct seq_file *)file->private_data)->private;
60	int val, rc;
61
62	rc = lprocfs_write_helper(buffer, count, &val);
63	if (rc)
64		return rc;
65	if (val < 0 || val > 1)
66		return -ERANGE;
67
68	/* opposite senses */
69	if (dev->u.cli.cl_import->imp_deactive == val)
70		rc = ptlrpc_set_import_active(dev->u.cli.cl_import, val);
71	else
72		CDEBUG(D_CONFIG, "activate %d: ignoring repeat request\n", val);
73
74	return count;
75}
76LPROC_SEQ_FOPS(osc_active);
77
78static int osc_max_rpcs_in_flight_seq_show(struct seq_file *m, void *v)
79{
80	struct obd_device *dev = m->private;
81	struct client_obd *cli = &dev->u.cli;
82	int rc;
83
84	client_obd_list_lock(&cli->cl_loi_list_lock);
85	rc = seq_printf(m, "%u\n", cli->cl_max_rpcs_in_flight);
86	client_obd_list_unlock(&cli->cl_loi_list_lock);
87	return rc;
88}
89
90static ssize_t osc_max_rpcs_in_flight_seq_write(struct file *file,
91			const char *buffer, size_t count, loff_t *off)
92{
93	struct obd_device *dev = ((struct seq_file *)file->private_data)->private;
94	struct client_obd *cli = &dev->u.cli;
95	struct ptlrpc_request_pool *pool = cli->cl_import->imp_rq_pool;
96	int val, rc;
97
98	rc = lprocfs_write_helper(buffer, count, &val);
99	if (rc)
100		return rc;
101
102	if (val < 1 || val > OSC_MAX_RIF_MAX)
103		return -ERANGE;
104
105	LPROCFS_CLIMP_CHECK(dev);
106	if (pool && val > cli->cl_max_rpcs_in_flight)
107		pool->prp_populate(pool, val-cli->cl_max_rpcs_in_flight);
108
109	client_obd_list_lock(&cli->cl_loi_list_lock);
110	cli->cl_max_rpcs_in_flight = val;
111	client_obd_list_unlock(&cli->cl_loi_list_lock);
112
113	LPROCFS_CLIMP_EXIT(dev);
114	return count;
115}
116LPROC_SEQ_FOPS(osc_max_rpcs_in_flight);
117
118static int osc_max_dirty_mb_seq_show(struct seq_file *m, void *v)
119{
120	struct obd_device *dev = m->private;
121	struct client_obd *cli = &dev->u.cli;
122	long val;
123	int mult;
124
125	client_obd_list_lock(&cli->cl_loi_list_lock);
126	val = cli->cl_dirty_max;
127	client_obd_list_unlock(&cli->cl_loi_list_lock);
128
129	mult = 1 << 20;
130	return lprocfs_seq_read_frac_helper(m, val, mult);
131}
132
133static ssize_t osc_max_dirty_mb_seq_write(struct file *file, const char *buffer,
134				      size_t count, loff_t *off)
135{
136	struct obd_device *dev = ((struct seq_file *)file->private_data)->private;
137	struct client_obd *cli = &dev->u.cli;
138	int pages_number, mult, rc;
139
140	mult = 1 << (20 - PAGE_CACHE_SHIFT);
141	rc = lprocfs_write_frac_helper(buffer, count, &pages_number, mult);
142	if (rc)
143		return rc;
144
145	if (pages_number <= 0 ||
146	    pages_number > OSC_MAX_DIRTY_MB_MAX << (20 - PAGE_CACHE_SHIFT) ||
147	    pages_number > totalram_pages / 4) /* 1/4 of RAM */
148		return -ERANGE;
149
150	client_obd_list_lock(&cli->cl_loi_list_lock);
151	cli->cl_dirty_max = (u32)(pages_number << PAGE_CACHE_SHIFT);
152	osc_wake_cache_waiters(cli);
153	client_obd_list_unlock(&cli->cl_loi_list_lock);
154
155	return count;
156}
157LPROC_SEQ_FOPS(osc_max_dirty_mb);
158
159static int osc_cached_mb_seq_show(struct seq_file *m, void *v)
160{
161	struct obd_device *dev = m->private;
162	struct client_obd *cli = &dev->u.cli;
163	int shift = 20 - PAGE_CACHE_SHIFT;
164	int rc;
165
166	rc = seq_printf(m,
167		      "used_mb: %d\n"
168		      "busy_cnt: %d\n",
169		      (atomic_read(&cli->cl_lru_in_list) +
170			atomic_read(&cli->cl_lru_busy)) >> shift,
171		      atomic_read(&cli->cl_lru_busy));
172
173	return rc;
174}
175
176/* shrink the number of caching pages to a specific number */
177static ssize_t osc_cached_mb_seq_write(struct file *file,
178				       const char __user *buffer,
179				       size_t count, loff_t *off)
180{
181	struct obd_device *dev = ((struct seq_file *)file->private_data)->private;
182	struct client_obd *cli = &dev->u.cli;
183	int pages_number, mult, rc;
184	char kernbuf[128];
185
186	if (count >= sizeof(kernbuf))
187		return -EINVAL;
188
189	if (copy_from_user(kernbuf, buffer, count))
190		return -EFAULT;
191	kernbuf[count] = 0;
192
193	mult = 1 << (20 - PAGE_CACHE_SHIFT);
194	buffer += lprocfs_find_named_value(kernbuf, "used_mb:", &count) -
195		  kernbuf;
196	rc = lprocfs_write_frac_helper(buffer, count, &pages_number, mult);
197	if (rc)
198		return rc;
199
200	if (pages_number < 0)
201		return -ERANGE;
202
203	rc = atomic_read(&cli->cl_lru_in_list) - pages_number;
204	if (rc > 0)
205		(void)osc_lru_shrink(cli, rc);
206
207	return count;
208}
209LPROC_SEQ_FOPS(osc_cached_mb);
210
211static int osc_cur_dirty_bytes_seq_show(struct seq_file *m, void *v)
212{
213	struct obd_device *dev = m->private;
214	struct client_obd *cli = &dev->u.cli;
215	int rc;
216
217	client_obd_list_lock(&cli->cl_loi_list_lock);
218	rc = seq_printf(m, "%lu\n", cli->cl_dirty);
219	client_obd_list_unlock(&cli->cl_loi_list_lock);
220	return rc;
221}
222LPROC_SEQ_FOPS_RO(osc_cur_dirty_bytes);
223
224static int osc_cur_grant_bytes_seq_show(struct seq_file *m, void *v)
225{
226	struct obd_device *dev = m->private;
227	struct client_obd *cli = &dev->u.cli;
228	int rc;
229
230	client_obd_list_lock(&cli->cl_loi_list_lock);
231	rc = seq_printf(m, "%lu\n", cli->cl_avail_grant);
232	client_obd_list_unlock(&cli->cl_loi_list_lock);
233	return rc;
234}
235
236static ssize_t osc_cur_grant_bytes_seq_write(struct file *file, const char *buffer,
237				  size_t count, loff_t *off)
238{
239	struct obd_device *obd = ((struct seq_file *)file->private_data)->private;
240	struct client_obd *cli = &obd->u.cli;
241	int		rc;
242	__u64	      val;
243
244	if (obd == NULL)
245		return 0;
246
247	rc = lprocfs_write_u64_helper(buffer, count, &val);
248	if (rc)
249		return rc;
250
251	/* this is only for shrinking grant */
252	client_obd_list_lock(&cli->cl_loi_list_lock);
253	if (val >= cli->cl_avail_grant) {
254		client_obd_list_unlock(&cli->cl_loi_list_lock);
255		return 0;
256	}
257	client_obd_list_unlock(&cli->cl_loi_list_lock);
258
259	LPROCFS_CLIMP_CHECK(obd);
260	if (cli->cl_import->imp_state == LUSTRE_IMP_FULL)
261		rc = osc_shrink_grant_to_target(cli, val);
262	LPROCFS_CLIMP_EXIT(obd);
263	if (rc)
264		return rc;
265	return count;
266}
267LPROC_SEQ_FOPS(osc_cur_grant_bytes);
268
269static int osc_cur_lost_grant_bytes_seq_show(struct seq_file *m, void *v)
270{
271	struct obd_device *dev = m->private;
272	struct client_obd *cli = &dev->u.cli;
273	int rc;
274
275	client_obd_list_lock(&cli->cl_loi_list_lock);
276	rc = seq_printf(m, "%lu\n", cli->cl_lost_grant);
277	client_obd_list_unlock(&cli->cl_loi_list_lock);
278	return rc;
279}
280LPROC_SEQ_FOPS_RO(osc_cur_lost_grant_bytes);
281
282static int osc_grant_shrink_interval_seq_show(struct seq_file *m, void *v)
283{
284	struct obd_device *obd = m->private;
285
286	if (obd == NULL)
287		return 0;
288	return seq_printf(m, "%d\n",
289			obd->u.cli.cl_grant_shrink_interval);
290}
291
292static ssize_t osc_grant_shrink_interval_seq_write(struct file *file,
293				const char *buffer, size_t count, loff_t *off)
294{
295	struct obd_device *obd = ((struct seq_file *)file->private_data)->private;
296	int val, rc;
297
298	if (obd == NULL)
299		return 0;
300
301	rc = lprocfs_write_helper(buffer, count, &val);
302	if (rc)
303		return rc;
304
305	if (val <= 0)
306		return -ERANGE;
307
308	obd->u.cli.cl_grant_shrink_interval = val;
309
310	return count;
311}
312LPROC_SEQ_FOPS(osc_grant_shrink_interval);
313
314static int osc_checksum_seq_show(struct seq_file *m, void *v)
315{
316	struct obd_device *obd = m->private;
317
318	if (obd == NULL)
319		return 0;
320
321	return seq_printf(m, "%d\n",
322			obd->u.cli.cl_checksum ? 1 : 0);
323}
324
325static ssize_t osc_checksum_seq_write(struct file *file, const char *buffer,
326			   size_t count, loff_t *off)
327{
328	struct obd_device *obd = ((struct seq_file *)file->private_data)->private;
329	int val, rc;
330
331	if (obd == NULL)
332		return 0;
333
334	rc = lprocfs_write_helper(buffer, count, &val);
335	if (rc)
336		return rc;
337
338	obd->u.cli.cl_checksum = (val ? 1 : 0);
339
340	return count;
341}
342LPROC_SEQ_FOPS(osc_checksum);
343
344static int osc_checksum_type_seq_show(struct seq_file *m, void *v)
345{
346	struct obd_device *obd = m->private;
347	int i;
348	DECLARE_CKSUM_NAME;
349
350	if (obd == NULL)
351		return 0;
352
353	for (i = 0; i < ARRAY_SIZE(cksum_name); i++) {
354		if (((1 << i) & obd->u.cli.cl_supp_cksum_types) == 0)
355			continue;
356		if (obd->u.cli.cl_cksum_type == (1 << i))
357			seq_printf(m, "[%s] ", cksum_name[i]);
358		else
359			seq_printf(m, "%s ", cksum_name[i]);
360	}
361	seq_printf(m, "\n");
362	return 0;
363}
364
365static ssize_t osc_checksum_type_seq_write(struct file *file, const char *buffer,
366				size_t count, loff_t *off)
367{
368	struct obd_device *obd = ((struct seq_file *)file->private_data)->private;
369	int i;
370	DECLARE_CKSUM_NAME;
371	char kernbuf[10];
372
373	if (obd == NULL)
374		return 0;
375
376	if (count > sizeof(kernbuf) - 1)
377		return -EINVAL;
378	if (copy_from_user(kernbuf, buffer, count))
379		return -EFAULT;
380	if (count > 0 && kernbuf[count - 1] == '\n')
381		kernbuf[count - 1] = '\0';
382	else
383		kernbuf[count] = '\0';
384
385	for (i = 0; i < ARRAY_SIZE(cksum_name); i++) {
386		if (((1 << i) & obd->u.cli.cl_supp_cksum_types) == 0)
387			continue;
388		if (!strcmp(kernbuf, cksum_name[i])) {
389		       obd->u.cli.cl_cksum_type = 1 << i;
390		       return count;
391		}
392	}
393	return -EINVAL;
394}
395LPROC_SEQ_FOPS(osc_checksum_type);
396
397static int osc_resend_count_seq_show(struct seq_file *m, void *v)
398{
399	struct obd_device *obd = m->private;
400
401	return seq_printf(m, "%u\n", atomic_read(&obd->u.cli.cl_resends));
402}
403
404static ssize_t osc_resend_count_seq_write(struct file *file, const char *buffer,
405			       size_t count, loff_t *off)
406{
407	struct obd_device *obd = ((struct seq_file *)file->private_data)->private;
408	int val, rc;
409
410	rc = lprocfs_write_helper(buffer, count, &val);
411	if (rc)
412		return rc;
413
414	if (val < 0)
415	       return -EINVAL;
416
417	atomic_set(&obd->u.cli.cl_resends, val);
418
419	return count;
420}
421LPROC_SEQ_FOPS(osc_resend_count);
422
423static int osc_contention_seconds_seq_show(struct seq_file *m, void *v)
424{
425	struct obd_device *obd = m->private;
426	struct osc_device *od  = obd2osc_dev(obd);
427
428	return seq_printf(m, "%u\n", od->od_contention_time);
429}
430
431static ssize_t osc_contention_seconds_seq_write(struct file *file, const char *buffer,
432				     size_t count, loff_t *off)
433{
434	struct obd_device *obd = ((struct seq_file *)file->private_data)->private;
435	struct osc_device *od  = obd2osc_dev(obd);
436
437	return lprocfs_write_helper(buffer, count, &od->od_contention_time) ?:
438		count;
439}
440LPROC_SEQ_FOPS(osc_contention_seconds);
441
442static int osc_lockless_truncate_seq_show(struct seq_file *m, void *v)
443{
444	struct obd_device *obd = m->private;
445	struct osc_device *od  = obd2osc_dev(obd);
446
447	return seq_printf(m, "%u\n", od->od_lockless_truncate);
448}
449
450static ssize_t osc_lockless_truncate_seq_write(struct file *file, const char *buffer,
451				    size_t count, loff_t *off)
452{
453	struct obd_device *obd = ((struct seq_file *)file->private_data)->private;
454	struct osc_device *od  = obd2osc_dev(obd);
455
456	return lprocfs_write_helper(buffer, count, &od->od_lockless_truncate) ?:
457		count;
458}
459LPROC_SEQ_FOPS(osc_lockless_truncate);
460
461static int osc_destroys_in_flight_seq_show(struct seq_file *m, void *v)
462{
463	struct obd_device *obd = m->private;
464	return seq_printf(m, "%u\n",
465			atomic_read(&obd->u.cli.cl_destroy_in_flight));
466}
467LPROC_SEQ_FOPS_RO(osc_destroys_in_flight);
468
469static int osc_obd_max_pages_per_rpc_seq_show(struct seq_file *m, void *v)
470{
471	return lprocfs_obd_rd_max_pages_per_rpc(m, m->private);
472}
473
474static ssize_t osc_obd_max_pages_per_rpc_seq_write(struct file *file,
475				const char *buffer, size_t count, loff_t *off)
476{
477	struct obd_device *dev = ((struct seq_file *)file->private_data)->private;
478	struct client_obd *cli = &dev->u.cli;
479	struct obd_connect_data *ocd = &cli->cl_import->imp_connect_data;
480	int chunk_mask, rc;
481	__u64 val;
482
483	rc = lprocfs_write_u64_helper(buffer, count, &val);
484	if (rc)
485		return rc;
486
487	/* if the max_pages is specified in bytes, convert to pages */
488	if (val >= ONE_MB_BRW_SIZE)
489		val >>= PAGE_CACHE_SHIFT;
490
491	LPROCFS_CLIMP_CHECK(dev);
492
493	chunk_mask = ~((1 << (cli->cl_chunkbits - PAGE_CACHE_SHIFT)) - 1);
494	/* max_pages_per_rpc must be chunk aligned */
495	val = (val + ~chunk_mask) & chunk_mask;
496	if (val == 0 || val > ocd->ocd_brw_size >> PAGE_CACHE_SHIFT) {
497		LPROCFS_CLIMP_EXIT(dev);
498		return -ERANGE;
499	}
500	client_obd_list_lock(&cli->cl_loi_list_lock);
501	cli->cl_max_pages_per_rpc = val;
502	client_obd_list_unlock(&cli->cl_loi_list_lock);
503
504	LPROCFS_CLIMP_EXIT(dev);
505	return count;
506}
507LPROC_SEQ_FOPS(osc_obd_max_pages_per_rpc);
508
509LPROC_SEQ_FOPS_RO_TYPE(osc, uuid);
510LPROC_SEQ_FOPS_RO_TYPE(osc, connect_flags);
511LPROC_SEQ_FOPS_RO_TYPE(osc, blksize);
512LPROC_SEQ_FOPS_RO_TYPE(osc, kbytestotal);
513LPROC_SEQ_FOPS_RO_TYPE(osc, kbytesfree);
514LPROC_SEQ_FOPS_RO_TYPE(osc, kbytesavail);
515LPROC_SEQ_FOPS_RO_TYPE(osc, filestotal);
516LPROC_SEQ_FOPS_RO_TYPE(osc, filesfree);
517LPROC_SEQ_FOPS_RO_TYPE(osc, server_uuid);
518LPROC_SEQ_FOPS_RO_TYPE(osc, conn_uuid);
519LPROC_SEQ_FOPS_RO_TYPE(osc, timeouts);
520LPROC_SEQ_FOPS_RO_TYPE(osc, state);
521
522LPROC_SEQ_FOPS_WR_ONLY(osc, ping);
523
524LPROC_SEQ_FOPS_RW_TYPE(osc, import);
525LPROC_SEQ_FOPS_RW_TYPE(osc, pinger_recov);
526
527static struct lprocfs_vars lprocfs_osc_obd_vars[] = {
528	{ "uuid",	     &osc_uuid_fops,	NULL, 0 },
529	{ "ping",	     &osc_ping_fops,    NULL, 0222 },
530	{ "connect_flags",   &osc_connect_flags_fops, NULL, 0 },
531	{ "blocksize",       &osc_blksize_fops,     NULL, 0 },
532	{ "kbytestotal",     &osc_kbytestotal_fops, NULL, 0 },
533	{ "kbytesfree",      &osc_kbytesfree_fops,  NULL, 0 },
534	{ "kbytesavail",     &osc_kbytesavail_fops, NULL, 0 },
535	{ "filestotal",      &osc_filestotal_fops,  NULL, 0 },
536	{ "filesfree",       &osc_filesfree_fops,   NULL, 0 },
537	/*{ "filegroups",      lprocfs_rd_filegroups,  NULL, 0 },*/
538	{ "ost_server_uuid", &osc_server_uuid_fops, NULL, 0 },
539	{ "ost_conn_uuid",   &osc_conn_uuid_fops, NULL, 0 },
540	{ "active",	     &osc_active_fops, NULL },
541	{ "max_pages_per_rpc", &osc_obd_max_pages_per_rpc_fops, NULL },
542	{ "max_rpcs_in_flight", &osc_max_rpcs_in_flight_fops, NULL },
543	{ "destroys_in_flight", &osc_destroys_in_flight_fops, NULL, 0 },
544	{ "max_dirty_mb",    &osc_max_dirty_mb_fops, NULL },
545	{ "osc_cached_mb",   &osc_cached_mb_fops, NULL },
546	{ "cur_dirty_bytes", &osc_cur_dirty_bytes_fops, NULL, 0 },
547	{ "cur_grant_bytes", &osc_cur_grant_bytes_fops, NULL },
548	{ "cur_lost_grant_bytes", &osc_cur_lost_grant_bytes_fops, NULL, 0},
549	{ "grant_shrink_interval", &osc_grant_shrink_interval_fops, NULL },
550	{ "checksums",       &osc_checksum_fops, NULL },
551	{ "checksum_type",   &osc_checksum_type_fops, NULL },
552	{ "resend_count",    &osc_resend_count_fops, NULL},
553	{ "timeouts",	     &osc_timeouts_fops, NULL, 0 },
554	{ "contention_seconds", &osc_contention_seconds_fops, NULL },
555	{ "lockless_truncate",  &osc_lockless_truncate_fops, NULL },
556	{ "import",		&osc_import_fops, NULL },
557	{ "state",		&osc_state_fops, NULL, 0 },
558	{ "pinger_recov",	&osc_pinger_recov_fops, NULL },
559	{ NULL }
560};
561
562LPROC_SEQ_FOPS_RO_TYPE(osc, numrefs);
563static struct lprocfs_vars lprocfs_osc_module_vars[] = {
564	{ "num_refs",	&osc_numrefs_fops,     NULL, 0 },
565	{ NULL }
566};
567
568#define pct(a, b) (b ? a * 100 / b : 0)
569
570static int osc_rpc_stats_seq_show(struct seq_file *seq, void *v)
571{
572	struct timeval now;
573	struct obd_device *dev = seq->private;
574	struct client_obd *cli = &dev->u.cli;
575	unsigned long read_tot = 0, write_tot = 0, read_cum, write_cum;
576	int i;
577
578	do_gettimeofday(&now);
579
580	client_obd_list_lock(&cli->cl_loi_list_lock);
581
582	seq_printf(seq, "snapshot_time:	 %lu.%lu (secs.usecs)\n",
583		   now.tv_sec, (unsigned long)now.tv_usec);
584	seq_printf(seq, "read RPCs in flight:  %d\n",
585		   cli->cl_r_in_flight);
586	seq_printf(seq, "write RPCs in flight: %d\n",
587		   cli->cl_w_in_flight);
588	seq_printf(seq, "pending write pages:  %d\n",
589		   atomic_read(&cli->cl_pending_w_pages));
590	seq_printf(seq, "pending read pages:   %d\n",
591		   atomic_read(&cli->cl_pending_r_pages));
592
593	seq_printf(seq, "\n\t\t\tread\t\t\twrite\n");
594	seq_printf(seq, "pages per rpc	 rpcs   %% cum %% |");
595	seq_printf(seq, "       rpcs   %% cum %%\n");
596
597	read_tot = lprocfs_oh_sum(&cli->cl_read_page_hist);
598	write_tot = lprocfs_oh_sum(&cli->cl_write_page_hist);
599
600	read_cum = 0;
601	write_cum = 0;
602	for (i = 0; i < OBD_HIST_MAX; i++) {
603		unsigned long r = cli->cl_read_page_hist.oh_buckets[i];
604		unsigned long w = cli->cl_write_page_hist.oh_buckets[i];
605		read_cum += r;
606		write_cum += w;
607		seq_printf(seq, "%d:\t\t%10lu %3lu %3lu   | %10lu %3lu %3lu\n",
608				 1 << i, r, pct(r, read_tot),
609				 pct(read_cum, read_tot), w,
610				 pct(w, write_tot),
611				 pct(write_cum, write_tot));
612		if (read_cum == read_tot && write_cum == write_tot)
613			break;
614	}
615
616	seq_printf(seq, "\n\t\t\tread\t\t\twrite\n");
617	seq_printf(seq, "rpcs in flight	rpcs   %% cum %% |");
618	seq_printf(seq, "       rpcs   %% cum %%\n");
619
620	read_tot = lprocfs_oh_sum(&cli->cl_read_rpc_hist);
621	write_tot = lprocfs_oh_sum(&cli->cl_write_rpc_hist);
622
623	read_cum = 0;
624	write_cum = 0;
625	for (i = 0; i < OBD_HIST_MAX; i++) {
626		unsigned long r = cli->cl_read_rpc_hist.oh_buckets[i];
627		unsigned long w = cli->cl_write_rpc_hist.oh_buckets[i];
628		read_cum += r;
629		write_cum += w;
630		seq_printf(seq, "%d:\t\t%10lu %3lu %3lu   | %10lu %3lu %3lu\n",
631				 i, r, pct(r, read_tot),
632				 pct(read_cum, read_tot), w,
633				 pct(w, write_tot),
634				 pct(write_cum, write_tot));
635		if (read_cum == read_tot && write_cum == write_tot)
636			break;
637	}
638
639	seq_printf(seq, "\n\t\t\tread\t\t\twrite\n");
640	seq_printf(seq, "offset		rpcs   %% cum %% |");
641	seq_printf(seq, "       rpcs   %% cum %%\n");
642
643	read_tot = lprocfs_oh_sum(&cli->cl_read_offset_hist);
644	write_tot = lprocfs_oh_sum(&cli->cl_write_offset_hist);
645
646	read_cum = 0;
647	write_cum = 0;
648	for (i = 0; i < OBD_HIST_MAX; i++) {
649		unsigned long r = cli->cl_read_offset_hist.oh_buckets[i];
650		unsigned long w = cli->cl_write_offset_hist.oh_buckets[i];
651		read_cum += r;
652		write_cum += w;
653		seq_printf(seq, "%d:\t\t%10lu %3lu %3lu   | %10lu %3lu %3lu\n",
654			   (i == 0) ? 0 : 1 << (i - 1),
655			   r, pct(r, read_tot), pct(read_cum, read_tot),
656			   w, pct(w, write_tot), pct(write_cum, write_tot));
657		if (read_cum == read_tot && write_cum == write_tot)
658			break;
659	}
660
661	client_obd_list_unlock(&cli->cl_loi_list_lock);
662
663	return 0;
664}
665#undef pct
666
667static ssize_t osc_rpc_stats_seq_write(struct file *file, const char *buf,
668				       size_t len, loff_t *off)
669{
670	struct seq_file *seq = file->private_data;
671	struct obd_device *dev = seq->private;
672	struct client_obd *cli = &dev->u.cli;
673
674	lprocfs_oh_clear(&cli->cl_read_rpc_hist);
675	lprocfs_oh_clear(&cli->cl_write_rpc_hist);
676	lprocfs_oh_clear(&cli->cl_read_page_hist);
677	lprocfs_oh_clear(&cli->cl_write_page_hist);
678	lprocfs_oh_clear(&cli->cl_read_offset_hist);
679	lprocfs_oh_clear(&cli->cl_write_offset_hist);
680
681	return len;
682}
683
684LPROC_SEQ_FOPS(osc_rpc_stats);
685
686static int osc_stats_seq_show(struct seq_file *seq, void *v)
687{
688	struct timeval now;
689	struct obd_device *dev = seq->private;
690	struct osc_stats *stats = &obd2osc_dev(dev)->od_stats;
691
692	do_gettimeofday(&now);
693
694	seq_printf(seq, "snapshot_time:	 %lu.%lu (secs.usecs)\n",
695		   now.tv_sec, (unsigned long)now.tv_usec);
696	seq_printf(seq, "lockless_write_bytes\t\t%llu\n",
697		   stats->os_lockless_writes);
698	seq_printf(seq, "lockless_read_bytes\t\t%llu\n",
699		   stats->os_lockless_reads);
700	seq_printf(seq, "lockless_truncate\t\t%llu\n",
701		   stats->os_lockless_truncates);
702	return 0;
703}
704
705static ssize_t osc_stats_seq_write(struct file *file, const char *buf,
706				   size_t len, loff_t *off)
707{
708	struct seq_file *seq = file->private_data;
709	struct obd_device *dev = seq->private;
710	struct osc_stats *stats = &obd2osc_dev(dev)->od_stats;
711
712	memset(stats, 0, sizeof(*stats));
713	return len;
714}
715
716LPROC_SEQ_FOPS(osc_stats);
717
718int lproc_osc_attach_seqstat(struct obd_device *dev)
719{
720	int rc;
721
722	rc = lprocfs_seq_create(dev->obd_proc_entry, "osc_stats", 0644,
723				&osc_stats_fops, dev);
724	if (rc == 0)
725		rc = lprocfs_obd_seq_create(dev, "rpc_stats", 0644,
726					    &osc_rpc_stats_fops, dev);
727
728	return rc;
729}
730
731void lprocfs_osc_init_vars(struct lprocfs_static_vars *lvars)
732{
733	lvars->module_vars = lprocfs_osc_module_vars;
734	lvars->obd_vars    = lprocfs_osc_obd_vars;
735}
736